이펙티브 타입스크립트 (댄 밴더캄 지음) 를 읽고 정리
📍타입 : 할당 가능한 값들의 집합
1️⃣모든 변수는
- 런타임 전에 타입을 갖는다
- 런타임 후에는 값을 갖는다
2️⃣타입의 범위
number 타입(집합)에 포함되는 것들 (부분집합)
- 42
- 37.25
- "Korea" 는 number의 부분집합이 아님
3️⃣never 타입 : 공집합
- 가장 작은 집합
- 아무 값도 할당할 수 없음
- 예시
const x: never = 12;
// 12 는 never 타입에 할당할 수 없음
4️⃣리터럴 타입 : 한가지 값만 포함하는 타입
- 유닛(unit) 타입이라고도 함
- 예시
type A = "A";
type B = "B";
type Twelve = 12;
- 2개 이상으로 묶으려면 유니온 타입을 사용
type AB = "A" | "B";
type AB12 = "A" | "B" | 12;
5️⃣unknown : 전체 집합
6️⃣구조적 타이핑 규칙
아래와 같은 타입이 있다고 가정할 때
interface Identified {
id: string;
}
어떤 객체가 string으로 할당 가능한 id 속성을 갖고 있다면, 그 객체는 Indetifed 타입이다
📍& 연산자
아래 2가지 타입이 있다고 할 때, &는 두 타입의 intersection (교집합) 을 가리킨다
interface Person {
name: string;
}
interface Lifespan {
birth: Date;
death?: Date;
}
type PersonSpan = Person & Lifespan;
const ps: PersonSpan = {
name: "Alan Turing",
birth: new Date("1912/06/23"),
death: new Date("1954/06/07"),
}; // 정상
// 앞의 3가지 보다 많은 프로퍼티를 가진 객체도 PersonSpan 타입이 될 수 있다
- 두 타입간 공통 프로퍼티가 없어서 never가 될 수 있어 보이지만,
& 연산자는 인터페이스의 프로퍼티가 아닌, 타입에 적용되므로, 합집합과 같은 역할을 한다 (구조적 타이핑을 떠올리자)
- 예시
string | number & string | Date = string
📍keyof
- keyof 연산자는 타입 객체의 키 값을 꺼낸다
interface Person {
name: string;
}
interface Lifespan {
birth: Date;
death?: Date;
}
type P = keyof Person // "name"
type L = keyof Lifespan // "birth", "death" (없어도 됨)
type K = keyof (Person | Lifespan) // never -> "name" 타입이면서 "birth" 타입일 수 없으므로
// (keyof Person) & (keyof Lifespan) 와 같음
type U = keyof (Person & Lifespan) // "name" | keyof Lifespan
// (keyof Person) | (keyof Lifespan) 와 같음
- keyof와 유니온, 인터섹션 타입의 관계는 아래와 같다
keyof (A&B) = (keyof A) | (keyof B)
keyog (A|B) = (keyof A) & (keyof B) -> never
- 따라서, 예시로 사용한 PersonSpan 타입을 일반적으로 사용하는 방법은 extends 키워드를 사용하는 것이다
📍extends
- 타입의 상속이라 생각할 수 있다
interface Person {
name: string;
}
// extends : Person 타입의 부분집합인 PersonSpan 타입~
interface PersonSpan extends Person {
birth: Date;
death?: Date;
}
- PersonSpan 타입은 name과 birth 프로퍼티를 가져야 한다
- PersonSpan 타입은 Person 타입의 서브 타입
- extends 키워드를 제네릭에도 사용할 수 있다
// K 타입은 string의 부분집합
function getKey<K extends string>(val: any, key: K) {
// ...
}
// 아래는 string의 부분집합인 타입
getKey({}, 'x'); // "x" 는 string의 부분집합
getKey({}, Math.random() < 0.5 ? "a" : "b"); // "a" | "b" 는 string의 부분집합
getKey({}, document.title); // string은 string의 부분집합
📍Tuple
타입이 집합이라는 관점에서 아래 코드를 보면 이해가 쉽다
const list = [1, 2]; // number[]
const tuple: [number, number] = list; // 에러 -> number[]가 [number, number]의 부분 집합이 아님
// 반대는 잘 동작한다