이펙티브 타입스크립트(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 를 전면 적용하기 전에 로컬에서부터 타입 오류를 점진적으로 수정해야 한다
◦
엄격한 타입 체크를 적용하기 전에 팀원들이 타입스크립트에 익숙해질 수 있도록 시간을 줘야 한다