Week 1. TypeApplier

BSK·2022년 3월 14일
0

Type of the week

목록 보기
1/1
post-thumbnail

Typescript에서는 union type(혹은 Discriminated Union)을 런타임에 구별하기 위해 별도의 field를 두도록 설계하는 것이 일반적입니다. 그러나 이러한 과정은 반복적이고, union type이 커질 수록 귀찮은 작업이 되기도 합니다.

TypeApplier는 이러한 수고를 덜기 위해 만든 타입입니다. 이 타입은 다른 타입에게 타입을 부여하는 타입입니다.

export type TypeApplier<V extends string, X, T> =
  T extends never
    ? Record<V, X>
    : Record<V, X> & T

이 TypeApplier Type는 3개의 타입 인자를 받습니다.

V: 구별에 사용할 키 값
X: union type의 값들을 구별할 수 있는 고유한 discriminator union
T: 그 외의 이 타입의 고유한 값

이 TypeApplier를 사용한 예시는 다음과 같습니다.

export type OptionalKind =
  'Some'
  | 'None'

export type OptionalTypeApplier<T extends OptionalKind, V> =
  TypeApplier<'optionalType', T, Record<'value', V>>

타입 Optional은 Some 과 None 타입의 값을 가지는 타입입니다.

Optional<T> = Some<T>| None

런타임에 이 타입의 값을 비교하기 위해서 이후에 optionalType 이라는 필드를 이용할 것입니다.

(그러지 않고서는 매번 런타임에 undefined와 null을 체크해야 하기 때문에)

type Some<T> = {
  optionalType:'Some'
  value:T
}

type None = {
  optionalType : 'None',
  value:undefined|null
}

따라서 이런 Optional type의 하위 타입들인 Some 타입과 None 타입을 정의하기 위해 필요한 optionalType 과 같은 필드를 삽입해 주는 OptionalTypeApplier 타입을 생성합니다. 이는 TypeApplier의 partial applied type 입니다.

이 OptionalTypeApplier 타입은 추후 Some 를 생성하는 데에 사용됩니다.

export type Some<T> = OptionalTypeApplier<'Some', T>

물론, 손을 이용하여 Some 혹은 None 타입을 직접 구현할 수 있습니다. 그러나 추후 Optional에 추가적인 하위 타입들(Some, None의 형제 타입들) 이 추가되는 시나리오에서, 수기로 작성되어 추가된 타입들이 optionalType 를 통해 구별될 수 있는 지 보장할 수 없습니다.

  type Some<T> = {
  optionalType:'Some'
  value:T
}

type None = {
  optionalType : 'None',
  value:undefined|null
}

type All<T> = {
  value:T
}

//type All cannot be discriminated by member 'optionalType'
type Optional<T> = Some<T>|None|All<T>

optionalType 필드만을 보장하기 위해 아래와 같이 사용하는 것도 위험합니다. 이는 discriminator가 해당 타입을 지칭하는 지 보장하지 않습니다.

type Some<T> = T

type None = undefined|null

type OptionalIntermediate<T> = Some<T>|None

type Optional<T> = OptionalIntermediate&{optionalType:OptionalKind}

이러한 TypeApplier를 통해 생성된 타입은 이런 위험을 안전하게 보장합니다.

type OptionalKind =
'Some'
| 'None'

type OptionalTypeApplier<T extends OptionalKind, V> =
TypeApplier<'optionalType', T, Record<'value', V>>

type Optional<T>=
OptionalTypeApplier<'Some',T>
| OptionalTypeApplier<'None',undefined|null>

물론, 추후 OptionalTypeApplier<'Some',T> 를 이쁘게 쓰기 위해서 별도의 type alias를 사용하겠지만, 이렇게 생성된 Optional Type은 Optional Kind에 의해 결정되고, 예외 타입들이 자동으로 보장되는 타입으로 구성됩니다.

profile
Biscuit Basket

0개의 댓글