📎아이템 14 타입 연산과 제너릭 사용으로 반복 줄이기
📍 DRY(don't repeat yourself) 원칙
원기둥의 반지름과 높이, 표면적, 부피를 출력하는 코드이다.
console.log(
'Cylinder r=1 × h=1',
'Surface area:', 6.283185 * 1 * 1 + 6.283185 * 1 * 1,
'Volume:', 3.14159 * 1 * 1 * 1
);
console.log(
'Cylinder r=1 × h=2',
'Surface area:', 6.283185 * 1 * 1 + 6.283185 * 2 * 1,
'Volume:', 3.14159 * 1 * 2 * 1
);
console.log(
'Cylinder r=2 × h=1',
'Surface area:', 6.283185 * 2 * 1 + 6.283185 * 2 * 1,
'Volume:', 3.14159 * 2 * 2 * 1
);비슷한 코드가 반복되어있다. 값과 상수가 반복되며 드러나지 않은 오류도 포함되어 있다. 이 코드에서 함수, 상수, 루프의 반복을 제거해 코드를 개선해보자.
type CylinderFn = (r: number, h: number) => number;
const surfaceArea: CylinderFn = (r, h) => 2 * Math.PI * r * (r + h);
const volume: CylinderFn = (r, h) => Math.PI * r * r * h;
for (const [r, h] of [[1, 1], [1, 2], [2, 1]]) {
console.log(
`Cylinder r=${r} × h=${h}`,
`Surface area: ${surfaceArea(r, h)}`,
`Volume: ${volume(r, h)}`);
}📍 타입에서의 DRY 원칙
타입 중복도 코드 중복만큼 많은 문제를 발생시킨다.
예를 들어, middleName을 Person에 추가한다고 하면 Person과 PersonWithBirthDate는 다른 타입이 된다.
🔗 타입에서의 중복
타입에서 중복이 흔한 이유는 공유된 패턴을 제거하는 매커니즘이 기존 코드에서 하던 것과 비교해 덜 익숙하기 때문이다. 타입 간의 매핑하는 방법을 익혀 타입 정의에서도 DRY의 장점을 적용해보자.
🔗 반복 줄이기
⭐️ 타입에 이름 붙이기
✓ 거리 계산 함수에서 타입이 반복적으로 등장한다.
타입에 이름을 붙여보자.
이 코드는 상수를 사용해서 반복을 줄이는 기법을 동일하게 타입 시스템에 적용한 것이다.
✓ 몇몇 함수가 같은 타입 시그니처를 공유하고 있는 경우
해당 시그니처를 명명된 타입으로 분리하자.
⭐️ 확장
✓ 위 Person과 PersonWithBirthDate 예제를 다시 보자
→ 한 인터페이스가 다른 인터페이스를 확장하게 해서 반복을 제거할 수 있다.
추가적인 필드만 작성하면 된다. 두 인터페이스가 필드의 부분 집합을 공유한다면, 공통 필드만 골라서 기반 클래스로 분리해 낼 수 있다.
✓ 이미 존재하는 타입을 확장하는 경우
→ & 연산자를 사용한다. (일반적❎) 유니온 타입(확장할 수 없는)에 속성을 추가할 때 특히 유용하다.
✓ 전체 애플리케이션의 상태를 표현하는 타입(State)과 부분만 표현(TopNavState)하는 타입
TopNavState를 확장하여 State를 구성하기보다, State의 부분 집합으로 TopNavState을 구성하는 것이 좋다. 이 방법이 전체 앱의 상태를 하나의 인터페이스로 유지할 수 있게 해준다.
State를 인덱싱하여 속성의 타입에서 중복을 제거할 수 있다.
State 내의 pageTitle 타입이 바뀌면 TopNavState에도 반영이 된다. 여전히 반복되는 코드가 존재한다.
'매핑된 타입'을 사용하자.
매핑된 타입은 배열의 필드를 루프 도는 것과 같은 방식이다. 이 패턴은 표준 라이브러리 Pick에서도 찾을 수 있다.
⭐️ 태그된 유니온에서의 중복
Action 유니온을 인덱싱하면 타입 반복 없이 ActionType을 정의할 수 있다.
Action 유니온에 타입을 더 추가하면 ActionType은 자동적으로 그 타입을 포함한다.
📍요약
DRY 원칙을 타입에도 최대한 적용하자.
타입에 이름을 붙여서 반복을 피해야 한다. extends를 사용해서 인터페이스 필드의 반복을 피해야한다.
타입들 간의 매핑을 위해 타입스크립트가 제공한 도구들을 공부하면 좋다. keyof, typeof, 인덱싱, 매핑된 타입들이 포함된다.
제너릭 타입은 타입을 위한 함수와 같다. 타입을 반복하는 대신 제너릭 타입을 사용하여 타입들 간에 매핑을 하는 것이 좋다. 제너릭 타입을 제한하려면 extends를 사용하면 된다.
표준 라이브러리에 정의된 Pick, Partial, ReturnType 같은 제너릭 타입에 익숙해지자.
Last updated