런타임에 타입을 보장하기 위해서 if문을 통한 속성 체크(property check)이외에도 tagged Union을 사용할 수 있다.
// property check
if (height in ...) {}
// tagged Union
interface Thing{
kind: 'square';
width: number;
height: number;
}
interface Thing{
kind: 'square';
width: number;
height: number;
}
if (X.kind === 'square'){}
조건문, typeof, instanceof등을 이용하여 타입을 좁히는(narrowing) 것을 말한다.
extends를 클래스의 상속개념으로 이해했을 때는 다음과 같은 코드를 이해하기 어려웠다.
function getKey<K extends string>(val: any, key: K) {
//...
}
그러나 집합으로 이해하면 extends가 '확장'이 아니라 '한정'의 의미로 쓰였다는 사실을 보다 받아들이기 쉬워진다.
보다 넓은 범위를 가지는 집합은 보다 '덜' 정의된 집합이다.
/* 아래에서 가장 넓은 집합은 Vector1D, 그 부분집합은 Vector2D, Vector3D다. */
interface Vector1D { x: number; }
interface Vector2D { x: number; y: number; }
interface Vector3D { x: number; y: number; z: number; }
/* 상단과 아래 코드는 같다. */
interface Vector1D { x: number; }
interface Vector2D extends Vector1D { y: number; }
interface Vector3D extends Vector2D { z: number; }
/*
* 튜플 타입도 생각해보자.
* number[]는 [number, number]의 부분집합이 아니다.
*/
argument부터 return타입까지 한꺼번에 정의할 수 있기 때문에 같은 유형의 함수를 반복해서 생성해야 한다면 함수 표현식이 더 유용하다.
type BinaryFn = (a:number, b:number) => number;
const add: BinaryFn = (a, b) => a + b;
const sub: BinaryFn = (a, b) => a - b;
const mul: BinaryFn = (a, b) => a * b;
const div: BinaryFn = (a, b) => a / b;