Blog

이펙티브 타입스크립트(8)

이펙티브 타입스크립트(8)

8-1. 모던 자바스크립트로 작성하기

타입스크립트는 자바스크립트의 상위 집합이기 때문에, 코드를 최신 버전으로 바꾸다 보면 타입스크립트의 일부를 저절로 익힐 수 있게 된다
모던 자바스크립트의 새로운 기능들은 모두 배울만 만한 가치가 있지만, 타입스크립트를 동비할 때 가장 중요한 기능은 ECMAScript 모듈과 ES2015 클래스이다

ECMAScript 모듈 사용하기

ES2015 부터는 import, export 를 사용하는 ECMAScript 모듈이 표준이 되었다
만약 마이그레이션 대상인 자바스크립트 코드가 단일 파일이거나 비표준 모듈을 사용중이라면 ES 모듈로 마이그레이션 하는게 좋다
ES 모듈 시스템은 타입스크립트에서도 잘 동작하며, 모듈 단위로 전환할 수 있게 해주기 때문에 점진적 마이그레이션이 원활해진다

프로토타입 대신 클래스 사용하기

과거에는 자바스크립트에서 프로토타입 기반의 객체 모델을 사용했었다
많은 개발자가 사용하기 애매한 프로토타입 모델보다는 견고하게 설계된 클래스 기반 모델을 선호했기 때문에 ES2015 에 클래스 키워드가 도입되었다
마이그레이셔낳려는 코드에서 단순한 객체를 다룰 때 프로토타입을 사용하고 있었다면 클래스로 마이그레이션하는게 좋다

var 대신 let/const 사용하기

var 대신 let, const 를 사용하면 스코프 문제를 회피할 수 있다
일부 코드에서 var 를 let, const 로 변경했을 떄 타입스크립트가 에러를 표시할 수 있는데, 이 부분은 스코프에 대한 문제가 있는 부분이기 때문에 반드시 수정해야 한다

for(;;) 대신 for-of 또는 배열 메서드 사용하기

과거 자바스크립트에서 배열을 순회할 때 C 스타일의 for loop 를 사용했다
예시 코드
for (var i = 0; i < array.length; i++) { const el = array[i]; // ... }
TypeScript
복사
모던 자바스크립트에는 for-of loop 를 사용한다
for-of 는 코드가 짧고 인덱스 변수를 사용하지 않기 때문에 실수를 방지할 수 있다
인덱스 변수가 필요한 경우 forEach 를 사용할 수 있다
예시 코드
for (const el of array) { // ... }
TypeScript
복사

함수 표현식 보다는 화살표 함수 사용하기

this 키워드는 일반적인 변수들과는 다른 스코프 규칙을 가지기 때문에, 자바스크립트에서 가장 어려운 개념 중 하나이다
일반적으로 this 가 클래스 인스턴스를 참조하는 것을 기대하지만, 예상치 못하는 결과가 나오는 경우도 있다
예시 코드
class Foo { method() { console.log(this); [1, 2].forEach(function(i) { console.log(this); }); } } const f = new Foo(); f.method(); // strict 모드에서는 Foo, undefined, undefined 를 출력 // non-strict 모드에서는 Foo, window, window 를 출력
TypeScript
복사
화살표 함수를 사용하면 상위 스코프의 this 를 유지할 수 있다
인라인(또는 콜백) 에서는 일반 함수보다 화살표 함수가 더 직관적이며 코드도 간결해지기 때문에 가급적 화살표 함수를 사용하는 것이 좋다
타입스크립트 컴파일 옵션에서 noImplicitThis 를 설정하면 타입스크립트가 this 바인딩 관련된 오류를 표시해주므로 설정하는 것이 좋다
예시 코드
class Foo { method() { console.log(this); [1, 2].forEach((i) => { console.log(this); }); } } const f = new Foo(); f.method(); // 항상 Foo, Foo, Foo 를 출력
TypeScript
복사

단순 객체 표현과 구조 분해 할당 사용하기

단순 객체 표현 (compact object literal)
변수와 객체 속성의 이름이 같다면 간단하게 작성할 수 있다
예시 코드
const x = 1, y = 2, z = 3; const pt = { x, y, z };
TypeScript
복사
구조 분해 할당 (object destructuring)
예시 코드
const obj = { props : { a: 1, b: 2, } } const { props } = obj; const { a, b } = props; // 더 극단적으로 const { props: { a, b }} = obj;
TypeScript
복사
기본 값 지정 예시 코드
const { a = 'default' } = obj;
TypeScript
복사
배열 예시 코드
const point = [1, 2, 3]; const [x, y, z] = point; const [, a, b] = point; // 첫번째 요소 무시
TypeScript
복사

함수 매개변수 기본값 사용하기

자바스크립트에서 함수의 모든 매개변수는 선택적이며, 매개변수를 지정하지 않으면 undefined 로 간주된다
모던 자바스크립트에서는 매개변수에 기본값을 직접 지정할 수 있다
예시 코드
function parseNum(str, base=10) { return parseInt(str, base); }
TypeScript
복사

저수준 프로미스나 콜백 대신 async/await 사용하기

콜백과 프로미스 대신 async/await 사용하는것을 권장한다
async/await 를 사용하면 코드가 간결해져서 버그나 실수를 방지할 숭 ㅣㅆ다
비동기 코드에 타입 정보가 전달되어 타입 추론을 가능하게 한다
예시 코드
async function getJSON(url: string) { const response = await fetch(url); return response.json(); }
TypeScript
복사

연관 배열에 객체 대신 Map, Set 사용하기

인덱스 시그니처를 편리하지만 몇가지 문제점이 있다
특정 주어진 문자열이 들어올 경우 문제가 발생한다
예시 코드
  Bad
function countwords(text: string) { const counts: {[word: string]: number} = {}; for (const word of text.split(/[\s,.]+/)) { counts[word] = 1 + (counts[word] || 0); } return counts; } console.log(countWords('Objects have a constructor'));
TypeScript
복사
{ Objects: 1, have: 1, a: 1, constructor: "1function Object() { [native code] }", }
TypeScript
복사
  Good
function countwords(text: string) { const counts = new Map<string, number>(); for (const word of text.split(/[\s,.]+/)) { counts.set(word, 1 + (counts.get(word) || 0)); } return counts; }
TypeScript
복사

타입스크립트에 use strict 넣지 않기

타입스크립트에서 수행되는 안정성 검사 (sanity check) 가 엄격 모드보다 훨씬 더 엄격한 체크를 하기 때문에, 타입스크립트 코드에서 use strict 는 무의미하다
타입스크립트 컴파일러가 생성하는 자바스크립트 코드에서 use strict 가 추가된다
타입스크립트 코드에 use strict 를 쓰지 않고, 대신 alwaysStrict 설정을 사용해야 한다

요약

타입스크립트 개발 환경은 모던 자바스크립트도 실행할 수 있으므로 모던 자바스크립트의 최신 기능들을 적극적으로 사용해야 한다

8-2. 타입스크립트 도입 전에 @ts-check 와 JSDoc 으로 시험해 보기

@ts-check 지시자를 사용하여 타입 체커가 파일을 분석하고, 발견된 오류를 보고하도록 할 수 있다
하지만, 매매우 느슨한 수준으로 타입 체크를 수행하며, noImplicitAny 설정을 해제한 것보다 헐거운 체크를 수행한다
예시 코드
// @ts-check const person = { first: 'GRace', last: 'Hopper' };
TypeScript
복사

요약

파일 상단에 @ts-check 를 사용하면 자바스크립트에서도 타입 체크를 할 수 있다
전역 선언과 서드파티 라이브러리의 타입 선언을 추가하는 방법을 알아야 한다
JSDoc 주석을 잘 활용하면 자바스크립트 상태에서도 타입 단언과 타입 추론을 할 수 있다
JSDoc 주석은 중간 단예기익 때문에 너무 공들일 필요는 없다

8-3. allowJS 로 타입스크립트와 자바스크립트 같이 사용하기

점진적으로 마이그레이션을 위해 자바스크립트와 타입스크립트를 동시에 사용할 수 있게 allowJs 컴파일러 옵션을 사용해야 한다
대규모 마이그레이션 작업을 시작하기 전에, 테스트와 빌드 체인에 타입스크립트를 적용해야 한다

8-4. 의존성 관계에 따라 모듈 단위로 전환하기

점진적 마이그레이션을 할 때는 모듈 단위로 각개격파 하는 것이 이상적이다
한 모듈을 골라서 타입 정보를 추가하면, 해당 모듈이 의존하는 모듈에서 비롯되는 타입 오류가 발생하게 된다
의존성과 관련된 오류없이 작업하려면, 다른 모듈에 의존하지 않는 최하단 모듈터 작업을 시작해서 의존성의 최상단에 있는 모듈을 마지막으로 완성해야 한다
마이그레이션을 할 때는 타입 정보만 추가하고, 리팩토링을 해서는 안된다

요약

마이그레이션의 첫 단계는, 서드파티 모듈과 외부 API 호출에 대한 @types 를 추가하는것이다
의존성 관계도의 아래에서부터 위로 올라가며 마이그레이션을 해야 한다
이상한 설계를 발견하더라도 리팩토링을 해서는 안된다
타입스크립트로 전환하며 발견하게 되는 일반적인 오류들을 놓치지 않아야 한다

8-5. 마이그레이션의 완성을 위해 noImplicitAny 설정하기

noImplicitAny 설정을 활성화하여 마이그레이션의 마지막 단계를 진행해야 한다
noImplicitAny 설정이 없다면 타입 선언과 관련된 실제 오류가 드러나지 않는다
noImplicitAny 를 전면 적용하기 전에 로컬에서부터 타입 오류를 점진적으로 수정해야 한다
엄격한 타입 체크를 적용하기 전에 팀원들이 타입스크립트에 익숙해질 수 있도록 시간을 줘야 한다