이펙티브 타입스크립트(7)
7-1. 타입스크립트 기능보다는 ECMAScript 기능을 사용하기
•
자바스크립트 초기에는 결함이 많고 기능이 부족했었다
◦
클래스, 데코레이터, 모듈시스템 같은 기능이 없어서 프레임워크나 트랜스파일러로 보완하는것이 일반적인 모습이였다
•
TC39(자바스크립트를 관장하는 표준 기구) 는 부족했떤 기능들을 대부분 내장 기능으로 추가했다
◦
자바스크립트 내장된 기능과 타입스크립트에서 구현된 기능이 충돌이 발생했고, 타입스크립트 팀은 대다수 하위호환성을 포기하고 자바스크립트 내장 기능을 따라가도록 구성했다
◦
타입스크립트팀은 타입 기능만 발전시킨다는 명확한 원칙을 세우고 현재까지 지켜오고 있다
열거형(enum)
•
몇몇 값의 모음을 나타내기 위해 열거형(enum)을 사용한다
◦
예시 코드
enum Flavor {
VANILLA = 0,
CHOCOLATE = 1,
STRAWBERRY = 2,
}
enum Flavor {
VANILLA = 'vanilla',
CHOCOLATE = 'chocolate',
STRAWBERRY = 'strawberry',
}
TypeScript
복사
•
일반적인 타입들이 할당 가능성을 체크하기 위해서 구조적 타이핑을 사용하는 반면, 문자열 열거형은 명목적 타이핑(nominally typing) 을 사용한다
•
열거형을 사용하게되면 실제 자바스크립트와 타입스크립트에서 동작이 다르기 때문에 사용하지 않는것이 좋다
◦
대신, 리터럴 타입의 유니온을 사용하면 된다
▪
예시 코드
type Flavor = 'vanilla' | 'chocolate' | 'strawberry';
let flavor: Flavor = 'chocolate';
TypeScript
복사
매개변수 속성
•
클래스를 초기화할 때 속성을 할당하기 위해 생성자의 매개변를 통해 손쉽게 사용할 수 있다
◦
예시 코드
class Person {
constructor(public name: string) {}
}
TypeScript
복사
◦
문제점
▪
타입스크립트 컴파일은 타입 제거가 이루어지므로 코드가 줄어들지만, 매개변수 속성값은 코드가 늘어나게 된다
▪
매개변수 속성이 런타임에서는 실제로 사용되지만, 타입스크립트 관점에서는 사용되지 않는 것처럼 보인다
▪
매개변수 속성과 일반 속성을 섞어서 사용하면 클래스의 설계가 혼란스러워 진다
•
매개변수 속성과 일반 속성을 같이 쓰게되면 코드가 정말 혼란스러워지기 때문에 한가지만 택해서 사용하는것이 좋다
◦
예시 코드
class Person {
first string;
last: string;
constructor(public name: string) {
[this.first, this.last] = name.split(' ');
}
}
TypeScript
복사
네임스페이스와 트리플 슬래시 임포트
•
ECMAScript 2015 이전에는 자바스크립트에 공식적인 모듈 시스템이 없었기 때문에 각 환경마다 자체적으로 모듈 시스템을 구현했다
◦
Node.js: require, module.exports
◦
AMD: define
◦
TypeScript: module, 트리플 슬래시 임포트
•
트리플 슬래시 임포트와 module 키워드는 호환성을 위해 남아 있을 뿐이며, 이제는 ECMAScript 2015 스타일의 모듈을 사용해야 한다
•
예시 코드
namespace foo {
function bar() {}
}
/// <reference path="other.ts"/>
foo.bar();
TypeScript
복사
데코레이터
•
클래스, 메서드, 속성에 애너테이션을 붙이거나 기능을 추가하는데 사용할 수 있다
•
데코레이터는 처음에 앵귤러 프레임워크를 지원하기 위해 추가되었다
•
현재까지도 표준화가 완료되지 않았기 때문에, 사용중인 데코레이터가 비표준으로 바뀌거나 호환성이 깨질 가능성이 있다
요약
•
일반적으로 타입스크립트 코드에서 모든 타입 정보를 제거하면 자바스크립트 코드가 되지만, 열거형, 매개변수 속성, 트리플 슬래시 임포트, 데코레이터는 타입 정보를 제거한다고 자바스크립트가 되진 않는다
•
타입스크립트의 역할을 명확하게 하려면 위의 언급한 기능들은 사용하지 않는것이 좋다
7-2. 객체를 순회하는 노하우
•
객체를 순회할 때, 키가 어떤 타입인지 정확히 파악하고 있다면 변수에 타입을 지정해주고 순회하면 된다
◦
예시 코드
interface ABC {
a: string;
b: string;
c: number;
}
function foo(abc: ABC) {
let k: keyof ABC;
for (k in abc) { // let k: "a" | "b" | "c"
const v = abc[k]; // string | number 타입
}
}
TypeScript
복사
•
타입 문제 없이 더 간단하게 사용하려면 Object.entries 를 사용하면 된다
◦
예시 코드
function foo(abc: ABC) {
for (const [k, v] of Object.entries(abc)) {
k // string
v // any
}
}
TypeScript
복사
요약
•
함수의 매개변수로 쓰이는 객체에는 추가적인 키가 존재할 수 있다는 점을 명심해야 한다
•
객체를 순회하며 키와 값을 얻는 가장 일반적인 방법은 Object.entries 를 사용하는 것이다
7-3. DOM 계층 구조 이해하기
요약
•
자바스크립트를 사용할 때는 신경쓰지 않겠지만, DOM 에는 타입 계층 구조가 있다
•
Node, Element, HTMLElement, EventTarget 간의 차이점, 그리고 Event 와 MouseEvent 의 차이점을 알아야 한다
•
DOM 엘리먼트와 이벤트에는 충분히 구체적인 타입 정보를 ㅏ용하거나, 타입스크립트가 추론할 수 있도록 문맥 정보를 활용해야 한다
7-4. 정보를 감추는 목적으로 private 사용하지 않기
•
자바스크리트는 클래스에 비공개 속성을 만들 수 없다
◦
관례적으로 앞에 _ 를 붙여서 적긴 했으나, 이건 단순 표시일 뿐이다
◦
타입스크립트에서도 private, public, protected 접근 제어를 사용해서 규칙을 강제할 수 없다
▪
컴파일하면 정보들이 사라지기 때문이다
•
정보를 감추기 위해서 private 을 사용하면 안된다
•
자바스크립트에서 정보를 숨기기 위해 가장 효과적인 방법은 클로저(closure) 를 사용하는 것이다
•
현재 표준화가 진행중인 비공개 필드 기능을 사용하는 것도 방법이다
◦
비공개 필드 기능은 접두사로 # 을 붙여서 타입 체크와 런타임 모두에서 비공개로 만드는 역할을 한다
◦
예시 코드
class PasswordChecker {
#passwordHash: number;
constructor(passwordHash: string) {
this.#passwordHash = passwordHash;
}
checkPassword(password: string) {
return hash(password) === this.#passwordHash;
}
}
TypeScript
복사
요약
•
public, protected, private 접근 제어자는 타입 시스템에서만 강제될 뿐이다
◦
런타임에서는 소용이 없으며, 타입 단언문으로 우회할 수 있다
•
확실히 데이터를 감추고 싶다면 클로저를 사용해야 한다
7-5. 소스맵을 사용하여 타입스크립트 디버깅하기
•
디버깅 문제를 해결하기 위해 브라우저 제조사들은 서로 협력하여 소스맵(source map) 이라는 해결책을 만들었다
•
소스맵은 변환된 코드의 우치와 심벌들을 원본 코드의 원래 위치와 심벌들로 매핑하는 역할을 한다
•
코드가 복잡하게 변환된다면 소스맵이 필요하다
요약
•
원본 코드가 아닌 변환된 자바스크립트 코드를 디버깅하지 말아야 한다
◦
소스맵을 사용해서 런타임에 타입스크립트 코드를 디버깅하면 된다
•
소스맵이 최종적으로 변환된 코드에 완전히 매핑되었는지 확인한다
•
소스맵에 원본 코드가 그대로 포함되도록 설정되어 있을 수 있다
◦
공개되지 않도록 설정을 확인해야 한다