이펙티브 타입스크립트 (댄 밴더캄 지음) 를 읽고 정리
📍요약
✅타입스크립트가 넓히기(type widening)를 통해 상수의 타입을 추론하는 법을 이해해야 한다
(const, at const 등)
📍런타임 전후의 변수
- 런타임 이후 : 유일한 값
- 런타임 이전 (정적 타입 검사) : 가능한 값들의 집합 (타입)
📍type widening
- 타입을 명시하지 않으면, 지정된 단일 값을 갖고 할당 가능한 범위를 유추하는 것
- 예시
const mixed = ["x", 1];
/*
mixed에 가능한 후보들
("x" | 1)[]
["x", 1]
[string, number]
readonly [string, number]
(string | number)[]
readonly (string | number)[]
[any, any]
any[]
*/
📍type widening의 과정을 제어하는 방법: const
- let 대신 const로 선언하여 타입을 좁게 만들기
- 예시
interface Vector3 { x: number; y: number; z: number; }
function getComponent(vector: Vector3, axis: 'x' | 'y' | 'z') { // axis 타입은 string이 아님에 주의
return vector[axis];
}
const x = 'x'; // type is "x" (let으로 선언하면 string)
let vec = {x: 10, y: 20, z: 30};
getComponent(vec, x); // OK
- x 가 재할당될 수 없으므로 더 좁은 타입으로 추론 가능 (string -> "x")
하지만, const 는 객체나 배열의 원소에 대해서는 한계를 가짐
- 예시) 아래 v 객체 리터럴의 타입은?
const v = {
x: 1,
};
// v의 타입 : {x: number}
v.x = 3; // OK
v.x = '3';
// ~ Type '"3"' is not assignable to type 'number'
v.y = 4;
// ~ Property 'y' does not exist on type '{ x: number; }'
v.name = 'Pythagoras';
// ~~~~ Property 'name' does not exist on type '{ x: number; }'
⭐type widening은 각 요소를 let으로 할당된 것처럼 다룸
- 따라서 타입이 {x: number}로 추론됨
- x 프로퍼티에 다른 number 는 재할당 가능하지만, 다른 타입은 불가
- 또한 x 이외 다른 프로퍼티를 추가할 수도 없음
📍타입추론의 강도 조절 방법 1 : 명시적 타입 구문
- 타입 명시를 통해 타입 확정
- 타입 : { x: 1|3|5 }
const v: { x: 1|3|5 } = {
x: 1,
}
📍타입추론의 강도 조절 방법 2 : 추가적 문맥 제공
타입 체커에 추가적 문맥 제공
- 예) 함수의 매개변수로 값을 전달 등
📍타입추론의 강도 조절 방법 3 : as const (const 단언문)
- 위에서 살펴봤던 const 와 다른 타입 공간에서의 const⭐값 뒤에 as const 를 붙이면, 타입스크립트는 최대한 좁은 타입으로 추론즉, type widening이 작동하지 않음- 예시1
const v1 = {
x: 1,
y: 2,
}; // Type is { x: number; y: number; }
const v2 = {
x: 1 as const,
y: 2,
}; // Type is { x: 1; y: number; }
const v3 = {
x: 1,
y: 2,
} as const; // Type is { readonly x: 1; readonly y: 2; }
- 예시2
튜플을 사용하여 길이를 고정
const a1 = [1, 2, 3]; // Type is number[]
const a2 = [1, 2, 3] as const; // Type is readonly [1, 2, 3] -> 튜플