Search
🍥

아이템 22. 타입 좁히기

타입 좁히기는 TS 이해에 중요하다
구별된 유니온과 사용자 정의 타입 가드는 원하는대로 타입을 좁히는데에 유용하다

1. TS의 다양한 타입 좁히기 과정

1.
null 체크
const el = document.getElementById('foo') // Type is HTMLElement | null if (el) { el.innerHTML = 'a' // el's type is 'HTMLElement' } else { alert('No #foo') // el's type is 'null' } // 위 내용은 아래와 같이 분기문에서 예외를 던지는 방식으로도 표현 가능 if (!el) throw new Error('No #foo') el.innerHTML = 'Party Time'.blink()
TypeScript
복사
2.
instanceof
function contains(text: string, search: string | RegExp) { if (search instanceof RegExp) { return !!search.exec(text) // Type of search is RegExp } return text.includes(search) // Type of search is string }
TypeScript
복사
3.
속성 체크
interface A { a: number } interface B { b: number } function pickAB(ab: A | B) { if ('a' in ab) { ab // Type is A } else { ab // Type is B } ab // Type is A | B }
TypeScript
복사
4. 타입 좁히기 시 나올 수 있는 실수
// null 또한 typeof의 결과는 object 임을 착각 const el = document.getElementById('foo') // type is HTMLElement | null if (typeof el === 'object') { el // Type is HTMLElement | null } function foo(x?: number | string | null) { if (!x) { // type of x is '' | 0 | null | undefined x // type of x is string | number | null | undefined } }
TypeScript
복사

2. 타입 좁히기에 유용한 테크닉

1.
명시적 태그 붙이기 (태그된 유니온 or 구별된 유니온)
interface UploadEvent { type: 'upload' filename: string contents: string } interface DownloadEvent { type: 'download' filename: string } type AppEvent = UploadEvent | DownloadEvent function handleEvent(e: AppEvent) { switch (e.type) { case 'download': e // Type is DownloadEvent break case 'upload': e // Type is UploadEvent break } }
TypeScript
복사
2.
타입 식별을 위한 커스텀 함수 도입 (사용자 정의 타입 가드)
// return 값이 true인 경우 매개변수의 타입을 좁힐 수 있다고 알려줌 // HTMLElement -> HTMLInputElement // is 앞에는 인자와 동일한 이름을 써주어야 함 function isInputElement(el: HTMLElement): el is HTMLInputElement { return 'value' in el } function getElementContent(el: HTMLElement) { if (isInputElement(el)) { el // Type is HTMLInputElement return el.value } el // Type is HTMLElement return el.textContent }
TypeScript
복사
3.
타입가드를 사용한 배열 및 객체의 타입 좁히기
const jackson5 = ['Jackie', 'Tito', 'Jermaine', 'Marlon', 'Michael'] // Type is (string | undefined)[] const members = ['Janet', 'Michael'] .map(who => jackson5.find(n => n === who)) // Type is (string | undefined)[] const members = ['Janet', 'Michael'] .map(who => jackson5.find(n => n === who)) .filter(who => who !== undefined) // 조건부로 반환값의 타입을 강제하는 커스텀 함수 도입 (사용자 정의 타입 가드) function isDefined<T>(x: T | undefined): x is T { return x !== undefined } // Type is string[] const members = ['Janet', 'Michael'] .map(who => jackson5.find(n => n === who)) .filter(isDefined)
TypeScript
복사