•
타입 좁히기는 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
복사