Blog

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

이펙티브 타입스크립트(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) 이라는 해결책을 만들었다
소스맵은 변환된 코드의 우치와 심벌들을 원본 코드의 원래 위치와 심벌들로 매핑하는 역할을 한다
코드가 복잡하게 변환된다면 소스맵이 필요하다

요약

원본 코드가 아닌 변환된 자바스크립트 코드를 디버깅하지 말아야 한다
소스맵을 사용해서 런타임에 타입스크립트 코드를 디버깅하면 된다
소스맵이 최종적으로 변환된 코드에 완전히 매핑되었는지 확인한다
소스맵에 원본 코드가 그대로 포함되도록 설정되어 있을 수 있다
공개되지 않도록 설정을 확인해야 한다