[Effective Typescript] 타입 연산과 제너릭 사용으로 반복 줄이기

이예슬·2022년 11월 13일
0

Effective TypeScript

목록 보기
5/15

아이템 14는 내용도 많고 개인적으로 배운 부분도 많아서 따로 작성했다.

아이템14. 타입 연산과 제너릭 사용으로 반복 줄이기

📌 DRY(don’t repeat yourself) : 같은 코드를 반복하지 말자는 원칙

반복된 코드를 반복하지 말자는 DRY 원칙은 타입에 대해서도 동일하게 적용된다. 타입 중복은 코드의 중복만큼 많은 문제를 발생시킨다.

타입간에 매핑을 사용하면 타입 정의에서도 불필요한 중복을 줄일 수 있다.

1. 타입에 이름 붙이기

중복을 피하는 가장 간단한 방법이다.

// 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) => { ...}

2. 타입을 확장해서 반복 제거하기

interface Person {
	firstName : string; 
	lastName : string; 
} 
interface PersonWithBirthDate extends Person { 
	birth: Date; 
} 

해당 방법을 사용하면 추가되는 필드만 작성한다. 만약 두 인터페이스가 필드의 부분 집합을 공유한다면 공통 필드만 골라서 기반 클래스로 분리할 수 있다.

이미 존재하는 타입을 확장하는 경우 & 연산자를 사용할 수도 있다.

// 자주사용되는 방식은 아니다. 
type PersonWithBirthDate = Person & {birth : Date} ; 

3. 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의 두 가지 타입을 받아서 결과 타입을 반환한다.

4. 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>;

5. typeof

값의 형태에 해당하는 타입을 정의하고 싶을 때는 typeof를 사용하면 된다.

const INIT_OPTIONS = { 
	width: 500,
	height: 500,
	color: '#fff',
} 
type Options = typeof INIT_OPTIONS

단 값으로부터 타입을 만들어 낼 때는 선언 순서에 주의해야 한다. 타입 정의를 먼저 하고 값이 그 타입에 할당 가능하다고 선언해야 타입이 더 명확해지고 예상하기 어려운 타입 변동을 방지할 수 있다.

✅ ReturnType : 함수 타입의 반환 타입으로 구성된 타입


<이펙티브 타입스크립트> Dan Vanderkam, 프로그래밍 인사이트 (2021)

profile
꾸준히 열심히!

0개의 댓글