이펙티브 타입스크립트 (댄 밴더캄 지음) 를 읽고 정리
📍요약
✅tagged union, discriminated union (특정 문자열을 타이핑에 활용) 및 타입 가드를 통해 type narrowing을 구현 가능
📍타입 좁히기(type narrowing)
- 가장 일반적인 예시 : null check (특정 엘리먼트가 있는지 여부)
- 분기문에 따라 타입을 좁힐 수 있음
const el = document.getElementById('foo'); // Type is HTMLElement | null
if (el) {
el // Type is HTMLElement
el.innerHTML = 'Party Time'
} else {
el // Type is null
alert('No element #foo');
}
// 엘리먼트가 없을 때 에러를 발생시키게 할 수도 있음
const el = document.getElementById('foo'); // Type is HTMLElement | null
if (!el) throw new Error('Unable to find #foo');
el; // Now type is HTMLElement
el.innerHTML = 'Party Time';
분기문 대신 instance 를 사용하는 방법도 있음
- 정규식 객체의 인스턴스이면 정규식, 아니면 문자열
function contains(text: string, search: string|RegExp) {
if (search instanceof RegExp) {
search // Type is RegExp
return !!search.exec(text);
}
search // Type is string
return text.includes(search);
}
Array.isArray 함수를 통해 배열인 경우 타입을 좁힐 수도 있음
⭐typeof 연산자를 활용하는 방법도 존재하지만, typeof null === "object" 임을 주의
매개변수의 경우, 없으면 undefined가 되는 것도 주의
- 예시
function foo(x?: number|string|null) { // 없을 수 있기 때문에 없는 경우 x 는 undefined
if (!x) {
x; // Type is string | number | null | undefined
}
}
📍tagged union (= discriminated union)을 활용한 type narrowing
- 특정 리터럴 타입을 활용
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;
}
}
📍타입 가드를 활용한 type narrowing1
아래 예시에서 is 키워드가 처음 등장
- is 키워드를 통해 반환 타입을 특정 조건에 대한 boolean으로 명시할 수 있다
function isInputElement(el: HTMLElement): el is HTMLInputElement {
// el (HTMLElement) 에 value 프로퍼티가 있으면 true, 없으면 false를 반환
// value 프로퍼티 O -> HTMLInputElement
// 함수의 반환 타입에서
// el === HTMLInputElement 또는
// el !== 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;
}
📍타입 가드를 활용한 type narrowing2
반환 값에서 undefined를 제거하고, 반환 타입로 문자열로 만드는 과정
const jackson5 = ['Jackie', 'Tito', 'Jermaine', 'Marlon', 'Michael'];
const members1 = ['Janet', 'Michael'].map(
who => jackson5.find(n => n === who) // find로 없으면 undefined 반환
); // Type is (string | undefined)[] -> 반환 타입을 string[]로 만들고 싶은데..
console.log(members1) // [undefined, "Michael"]
const members2 = ['Janet', 'Michael'].map(
who => jackson5.find(n => n === who)
).filter(who => who !== undefined); // Type is (string | undefined)[]
// 반환값에서 undefined를 빼긴 했지만, 반환 타입을 string[]로 만들고 싶은데..
console.log(members2) // ["Michael"]
function isDefined<T>(x: T | undefined): x is T {
return x !== undefined;
}
const members3 = ['Janet', 'Michael'].map(
who => jackson5.find(n => n === who)
).filter(isDefined); // Type is string[] -> 성공
console.log(members3) // ["Michael"]