아이템 14는 내용도 많고 개인적으로 배운 부분도 많아서 따로 작성했다.
반복된 코드를 반복하지 말자는 DRY 원칙은 타입에 대해서도 동일하게 적용된다. 타입 중복은 코드의 중복만큼 많은 문제를 발생시킨다.
타입간에 매핑을 사용하면 타입 정의에서도 불필요한 중복을 줄일 수 있다.
중복을 피하는 가장 간단한 방법이다.
// before
function get(url: string, opts: Options) : Promise<Response> { ... }
function post(url: string, opts: Options) : Promise<Response> { ... }
//after
type HTTPFunction = (url: string, opts: Options) => Promise<Response> ;
const get: HTTPFunction = (url, opts) => { ...}
const post: HTTPFunction = (url, opts) => { ...}
interface Person {
firstName : string;
lastName : string;
}
interface PersonWithBirthDate extends Person {
birth: Date;
}
해당 방법을 사용하면 추가되는 필드만 작성한다. 만약 두 인터페이스가 필드의 부분 집합을 공유한다면 공통 필드만 골라서 기반 클래스로 분리할 수 있다.
이미 존재하는 타입을 확장하는 경우 &
연산자를 사용할 수도 있다.
// 자주사용되는 방식은 아니다.
type PersonWithBirthDate = Person & {birth : Date} ;
Pick
: 전체의 부분 집합으로 타입을 사용하기전체 앱의 상태를 하나의 인터페이스로 유지하기 위해 확장이 아닌 전체의 부분집합의 타입을 만들어서 사용할 수도 있다.
interface State {
userId: string;
pageTitle: string;
recentFiles: string[];
pageContents: string;
}
type TopNavState = {
userId: State['userId']
pageTitle: State['pageTitle']
}
// 매핑된 타입
type TopNavState = {
[k in 'userId' | 'pageTitle' ] : State[k]
}
위와 같은 매핑된 타입은 배열의 필드를 루프 도는 것과 같은 방식이다.
매핑된 타입을 표준 라이브러리에서도 찾을 수 있다.
type TopNavState = Pick<State, 'userId' | 'pageTitle'> ; // es2015
즉 특정 타입에서 몇 개의 속성을 선택하여 타입을 정의하고 싶을 때 Pick
을 사용하면 된다. (Omit
과 반대)
이 때 Pick
은 제너릭 타입이며 T, K의 두 가지 타입을 받아서 결과 타입을 반환한다.
Partial
: 선택적 타입아래와 같이 생성하고 난 후 업데이트가 되는 클래스를 정의해야 할 경우 update 메서드의 매개변수 타입은 생성자와 동일한 매개변수이면서 타입 대부분이 선택적 필드가 된다.
interface Options {
width: number;
height: number;
color: number;
}
interface OptionsUpdate {
width: number;
height: number;
color: number;
}
이 때 매핑된 타입과 keyof를 사용하면 불필요한 중복을 피할 수 있다.
type OptionsUpdate = {[k in keyof Options]? : Options[k]}
keyof 는 타입을 받아서 속성 타입의 유니온을 반환한다. 위 코드에서 매핑된 타입은 순회하며 Optinos 내 k 값에 해당하는 속성이 있는지 찾고 이 때 ? 는 각 속성을 선택적으로 만듭니다.
위 패턴 역시 표준 라이브러리에 Partial
라는 이름으로 존재한다.
type OptionsUpdate = Partial<Options>;
typeof
값의 형태에 해당하는 타입을 정의하고 싶을 때는 typeof를 사용하면 된다.
const INIT_OPTIONS = {
width: 500,
height: 500,
color: '#fff',
}
type Options = typeof INIT_OPTIONS
단 값으로부터 타입을 만들어 낼 때는 선언 순서에 주의해야 한다. 타입 정의를 먼저 하고 값이 그 타입에 할당 가능하다고 선언해야 타입이 더 명확해지고 예상하기 어려운 타입 변동을 방지할 수 있다.
✅ ReturnType
: 함수 타입의 반환 타입으로 구성된 타입
<이펙티브 타입스크립트> Dan Vanderkam, 프로그래밍 인사이트 (2021)