반응형
TLDR : CIF와 레코드 타입의 키타입 정의를 피하자.
Typescript를 쓰다보면 똑같은 타입을 반복해서 작성하는 거지같은 짓을 많이 본다.
잘못된 타입을 여러곳에서 여러 사람들이 다른 이름으로 쓰는 것도 많이 보게된다.
타입 관리도 비용이다.
How to write a Constrained Identity Function (CIF) in TypeScript (kentcdodds.com)
해당 게시물의 요약본이다.
문제점
const operations = {
'+': (left: number, right: number): number => left + right,
'-': (left: number, right: number): number => left - right,
'*': (left: number, right: number): number => left * right,
'/': (left: number, right: number): number => left / right,
}
type CalculatorProps = {
left: number
operator: keyof typeof operations
right: number
}
function Calculator({left, operator, right}: CalculatorProps) {
const result = operations[operator](left, right)
return (
<div>
<code>
{left} {operator} {right} = <output>{result}</output>
</code>
</div>
)
}
const examples = (
<>
<Calculator left={1} operator="+" right={2} />
<Calculator left={1} operator="-" right={2} />
<Calculator left={1} operator="*" right={2} />
<Calculator left={1} operator="/" right={2} />
</>
)
해당 컴포넌트를 타이핑한다고 생각하자.
type OperationFn = (left: number, right: number) => number
type Operator = '+' | '-' | '/' | '*'
const operations: Record<Operator, OperationFn> = {
'+': (left, right) => left + right,
'-': (left, right) => left - right,
'*': (left, right) => left * right,
'/': (left, right) => left / right,
}
type CalculatorProps = {
left: number
operator: keyof typeof operations
right: number
}
operations 추가 시 Operator 타입을 추가한다. 귀찮다.
목표
1. "keyof typeof Operator"는 union(product) 타입
2. 각 프로퍼티의 타입은 동일. (Record<A,B>)
문제점 2
문제점1의 예제는 목표 2 달성
아래는 아님...
(타입스크립트는 가능한 한 넓게 타입을 추론하기 때문)
type OperationFn = (left: number, right: number) => number
const operations: Record<string, OperationFn> = {
'+': (left, right) => left + right,
'-': (left, right) => left - right,
'*': (left, right) => left * right,
'/': (left, right) => left / right,
}
type a = keyof typeof operations; // a : string
해결방안
타입스크립트에게 키 타입 추론을 좁히고, 벨류 타입 범위를 넓히도록 하자.
CIF(씨 아이 에프)
type Value = number
const createNumbers = <ObjectType extends Record<string, Value>>(
obj: ObjectType,
) => obj
type OperationFn = (left: number, right: number) => number
const createOperations = <OperationsType extends Record<string, OperationFn>>(
operations: OperationsType,
) => operations
type a = keyof typeof operations; // a : '+' | '-' | '/' | '*'
조금 더 일반화한 방법
const constrain =
<Given extends unknown>() =>
<Inferred extends Given>(item: Inferred) =>
item
const numbers = constrain<Record<string, number>>()({one: 1 /* etc. */})
// or
const createNumbers = constrain<Record<string, number>>()
const numbers = createNumbers({one: 1 /* etc. */})
결론 : 타입스크립트의 진화는 계속될 필요가 있다.
반응형
'FrontEnd' 카테고리의 다른 글
8. 커스텀 훅 테스트 (0) | 2021.12.23 |
---|---|
Ad hoc polymophism(애드훅 다형성) with typescript (0) | 2021.12.10 |
타입스크립트 의존성 주입, 제어의 역전 with IOC 컨테이너 (0) | 2021.12.06 |
브라우저에서 ESModule 사용하기 (0) | 2021.12.05 |
Node.js 메모리 누수 찾기 관련 게시글 모음 (0) | 2021.12.05 |