Skip to content

TypeScript 타입 좁히기 해설

TypeScript가 분기 안에서 타입을 좁히는 방법을 기법별로 정리했습니다. 내장 가드, 커스텀 서술, 어설션 함수, 구별된 유니언, 동등성과 truthiness 흐름, 그리고 별칭과 지연 좁히기입니다. 각 행은 기법과 한 줄 예시를 짝지었습니다.

좁히기(narrowing)는 TypeScript가 코드 분기 안에서 넓은 타입을 더 좁은 타입으로 바꿔 속성 접근이 안전함을 증명하게 하는 방식입니다. 모든 검사(typeof, in, 서술, 판별자)는 컴파일러에게 '이 지점부터 값이 더 구체적이다'라고 알립니다. 아래 기법이 실무에서 쓰는 세트입니다.

참고 표 · 23 항목
23 of 23 rows
내장 가드
프리미티브를 좁힙니다: string, number, boolean 등.if (typeof x === 'string') x.toUpperCase()
클래스 또는 생성 가능한 인스턴스로 좁힙니다.if (e instanceof Error) e.message
속성이 값에 존재하는지 확인하여 좁힙니다.if ('id' in obj) obj.id
모든 값을 배열로 좁힙니다. 유니언은 배열 멤버로 좁혀집니다.if (Array.isArray(x)) x.map(f)
커스텀 가드
반환값이 인수를 좁히는 서술 함수입니다.function isUser(x): x is User { return !!x.id }
서술은 단일 타입뿐 아니라 타입 유니언으로도 좁힐 수 있습니다.function isKey(x): x is string | number
교차 타입 서술은 입력 타입을 유지한 채 필드를 추가합니다.function hasId(x): x is x & { id: string }
x가 해당 타입이 아니면 예외를 던지는 어설션 함수입니다.function assertUser(x): asserts x is User
구별된 유니언
switch나 if로 공유 리터럴 필드를 통해 유니언을 좁힙니다.type E = { kind: 'a'; a: number } | { kind: 'b'; b: string }
각 case는 유니언의 한 멤버로 좁힙니다.switch (u.kind) { case 'a': u.a }
속성이 한 멤버에만 존재할 때 태그 없는 유니언을 좁힙니다.if ('b' in u) u.b
각 case는 조건입니다. 매칭된 case가 값을 좁힙니다.switch (true) { case typeof x === 'string': }
default 케이스에서 never에 할당하여 switch가 완전함을 증명합니다.default: { const x: never = u }
동등성과 흐름
동등성 검사 후 null과 undefined를 제거합니다.if (x === null) return; x.toFixed()
falsy 값(0, '', null, undefined, NaN)을 제거합니다.if (!x) return; // x is defined here
truthy인 체인 속성은 기반 값이 존재함을 증명합니다.if (user?.active) user.name
논-널 어설션은 검사 없이 null과 undefined를 제거합니다.x!.toFixed(2)
확정 할당: x를 읽기 전에 할당된다고 선언합니다.let name!: string; init()
좁히기는 분기 안과 조기 return 이후에 적용됩니다.if (!arr) return; arr.map(...)
별칭과 지연
검사를 담은 const 불리언은 평가되는 어디서든 좁힙니다.const isStr = typeof x === 'string'; if (isStr) …
const 변수는 자신을 캡처하는 클로저 안에서 좁히기를 유지합니다.const s = get(); if (s) use(() => s.length)
TS 5.4부터 let이나 매개변수는 마지막 할당 후에도 클로저에서 좁히기를 유지합니다.let n: string | null = get(); if (n) use(() => n.length)
unknown은 가드가 실제 타입으로 좁히기 전까지 사용할 수 없습니다.if (typeof u === 'string') u.length