[2장] 타입스크립트의 타입 시스템 (1 / 2)이펙티브 타입스크립트 2장의 아이템 1 - 11을 정리한 내용입니다.작성일 2024.02.14페이지가 생성된 시간 2024.11.16 01:32:34
0
2장의 핵심
- 타입시스템의 기초
- 타입시스템이 뭔지, 어떻게 사용하는지, 가급적 사용하지 말아야 할 기능은 뭔지
- 이 책의 나머지를 읽을 때 필요한 개념의 토대이다
아이템6
편집기를 사용하여 타입시스템 탐색하기
- 타입스크립트는 알아서 타입 추론을 함.. 편집기의 언어서비스를 통해 확인이 가능
- 타입 선언 파일을 찾아보면 라이브러리가 어떻게 모델링 되었는지, 어떻게 오류를 찾아낼지 살펴볼 수 있다.
// Request를 생성할 때 사용할 수 있는 옵션
interface RequestInit {
body?: BodyInit | null;
cache?: RequestCache;
credentials?: RequestCredentials;
headers?: HeadersInit;
// ...
}
아이템7
타입이 값들의 집합이라고 생각하기
- 타입은 '할당가능한 값들의 집합'
- 타입체커의 역할은 하나의 집합이 다른 집합의 부분집합인지를 검사하는 것
- extends의 의미: '~의 부분집합'
- 타입스크립트 타입이 되지 못하는 값의 집합이 존재한다.
- ex: 정수에 대한 타입, x, y만을 속성으로 가지는 객체 등
// 속성(프로퍼티)들의 집합이 아니라 타입이 될 수 있는 값의 집합임을 이해하기
interface Person {
name: string;
}
interface Lifespan {
birth: Date;
death?: Date;
}
type PersonSpan = Person & Lifespan;
type K = keyof (Person | Lifespan); // Type is never
// extends가 제네릭 타입에서 한정자로 쓰인다
function getKey<K extends string>(val: any, key: K) {
// ...
}
getKey({}, 'x'); // OK, 'x' extends string
getKey({}, Math.random() < 0.5 ? 'a' : 'b'); // OK, 'a'|'b' extends string
getKey({}, document.title); // OK, string extends string
getKey({}, 12);
// ~~ Type '12' is not assignable to parameter of type 'string'
// 타입이 되지 못하는 값의 집합 예시
type T = Exclude<string | Date, string | number>; // Type is Date
type NonZeroNums = Exclude<number, 0>; // Type is still just number
아이템8
타입공간의 심벌인지 값공간의 심벌인지 구분 잘 하기
- 자바스크립트로 변환된 코드를 보면 어떤 공간인지 알 수 있다.
- 일반적으로 type, interface 뒤는 타입심벌, let, const 뒤는 값심벌
- class, enum은 타입과 값 두 가지 모두 가능
- typeof 연산자는 타입공간에서는 타입스크립트 타입을 반환하지만, 값공간(자바스크입트 런타임)에서는 자바스크립트의 타입(문자열로 된)을 반환한다.
- 클래스 자체의 타입은 typeof 붙여야됨.. 디폴트는 인스턴스의 타입이다. InstanceType을 통해 클래스 타입 -> 인스턴스 타입으로 전환 가능
- 타입 공간에서 속성접근자는 . 말고 [] 사용해라
- as const 는 타입추론이 바뀐다.
type User = {
name: string;
age: number;
};
const user: User = {
name: 'jinwook',
age: 20,
};
type User = {
readonly name: 'jinwook';
readonly age: 20;
};
const user: User = {
name: 'jinwook',
age: 20,
} as const;
this
타입 (Polymorphic this types)
다형성 다형성 this 타입은 포함하는 클래스나 인터페이스의 하위 타입을 나타냅니다. F-bounded polymorphism이라고 부릅니다. 예를 들어, 계층적으로 유연한 인터페이스를 표현하기 더 쉽게 만듭니다. 각 연산 후에 this를 반환하는 간단한 계산기를 보겠습니다
class BasicCalculator {
public constructor(protected value: number = 0) {}
public currentValue(): number {
return this.value;
}
public add(operand: number): this {
this.value += operand;
return this;
}
public multiply(operand: number): this {
this.value *= operand;
return this;
}
// ... 다른 연산들은 여기에 작성 ...
}
let v = new BasicCalculator(2).multiply(5).add(1).currentValue();
클래스가 this 타입을 사용하기 때문에, 이를 extend 할 수 있고 새로운 클래스가 아무 변경 없이 이전 메서드를 사용할 수 있습니다.
class ScientificCalculator extends BasicCalculator {
public constructor(value = 0) {
super(value);
}
public sin() {
this.value = Math.sin(this.value);
return this;
}
// ... 다른 연산들은 여기에 작성 ...
}
let v = new ScientificCalculator(2).multiply(5).sin().add(1).currentValue();
this 타입 없이, ScientificCalculator는 BasicCalculator를 extend 할 수 없을 것이고 유연한 인터페이스를 유지하지 못할 것입니다. multiply는 sin 메서드를 가지지 않는 BasicCalculator를 반환합니다. 하지만, this 타입으로, multiply는 this를 반환하고, 여기서는 ScientificCalculator을 말합니다.
아이템9
타입단언 보다는 타입선언을
- 타입단언을 쓰지 말고 타입선언으로 되는지 꼭 먼저 해보자
- 체이닝이 연속되는 곳이 있다면 체이닝 시작부터 명명된 타입을 가져야 함. 그래야 오류가 정확한 곳에 표시됨
- DOM엘리먼트 접근 등은 타입스크립트는 모르고 나만 아는 정보로 인해(실제 DOM의 생김새) 타입단언이 필요할 때가 있음
- 분명히 DOM에는 엘리먼트가 존재하는데 타입스크립트는 undefined 일 수도 있다고 한다.
- 접미사 '!' => nonnull 단언을 통해 알려주자
- B as A => A가 B의 부분집합인 경우 또는 B가 A의 부분집합인 경우에만 타입단언이 가능하다. (서로의 서브타입일 경우라고 표현)
아이템10
객체 래퍼 타입 입히기
- 자바스크립트의 객체 래퍼 동작 이해하기
- 그리고 그걸 타입스크립트의 타입으로 쓰는것 피하기
아이템11
잉여 속성 체크의 한계
- 할당 가능 검사와 잉여 속성 체크(엄격한 객체 리터럴 체크)는 별도의 과정
- 임시 변수 사용 시 잉여 속성 체크 건너뜀
- 타입 단언 시 잉여 속성 체크는 동작하지 않음
- 약한 타입: 선택적 속성만 가지는 타입
- 약한 타입에서는 공통된 속성 검사가 들어간다. 임시변수를 사용해도 적용된다
- 약한 타입에서는 임시 변수로 할당시에도 잉여 속성 체크와 비슷한 체크가 일어나는데 그걸 공통된 속성 체크라고 하는듯함
interface A {
a?: string;
b?: string;
}
const a = {};
const b = { c: 'a' };
const c: A = b; // 이런걸 임시변수를 사용한 할당이라고 함..
// ~~ '{ c: string; }' 유형에 'A' 유형과 공통적인 속성이 없습니다.