Conditional types in which the checked type is a naked type parameter are called distributive conditional types. Distributive conditional types are automatically distributed over union types during instantiation.
Checked Type
type A<T> = string extends T ? "yes" : "no"
type B<T> = { x: T } extends { x: number } ? "yes" : "no"
type C<T, U> = T extends U ? "yes" : "no"
제네릭 타입에서 제약 조건을 검사하는 타입을 말하며, extends 키워드 앞에 나오는 타입을 말함. 예를 들어, A<T>에서는 string, B<T>에서는 { x: T }가 Checked Type이 됨.
Naked Type Parameter 제네릭 타입에서 타입 파라미터 각각을 말함. A<T>, B<T>에서는 T, C<T, U>에서는 T와 U가 Naked Type Parameter에 해당함.
위 조건에 해당하는 제네릭 타입이라면 MyUnion이 A | B | C의 유니온 타입일 때, D<MyUnion>의 결과는 아래와 같음.
type D<T> = T extends U ? X : Y
type D<MyUnion> = (A extends U ? X : Y) | (B extends U ? X : Y) | (C extends U ? X : Y)
A. Distributive Conditional Type 비활성화하기
타입 파라미터가 유니온 타입일 때 제네릭 타입이 유니온 멤버마다 적용되는 게 합리적이지만 경우에 따라서 원하지 않는 동작이 될 수 있음. 그런 경우에는 Distributive Conditional Type은 Checked Type이 Naked Type Parameter일 때 적용되므로 Checked Type이 Naked Type Parameter가 아니도록 변경해주면 됨.
type MyConditionalType1<T> = T extends string ? 'string' : 'nope'
type MyConditionalType2<T> = [T] extends [string] ? 'string' : 'nope'
type Param = string | number
type T1 = MyConditionalType1<Param> // 'string' | 'nope'
type T2 = MyConditionalType2<Param> // 'nope'
interface Foo {
foo: string
}
interface Bar {
bar: string
}
type MyObject = Foo | Bar
type KeysOfMyObject1 = keyof MyObject // never
객체의 유니온 타입에 대해 모든 객체들의 key 값 타입을 얻고 싶을 때 단순하게 위와 같이 작성할 수 있지만 그 결과는 never 타입. 유니온 타입에 대한 keyof는 모든 객체들의 key 타입이 아니라 공통된 key 타입이기 때문. 공통돤 key 타입이 아니라 모든 key 타입을 얻고 싶다면 Distributive Conditional Type에서는 각각의 유니온 멤버들에 대해 Conditional Type이 적용된다는 점을 이용하는 방법이 있음.
type ExtendsNever<P = never> = P extends never ? true : false
const test: ExtendsNever = true // never
위와 같이 제네릭 타입의 기본 파라미터로 never 타입을 사용했을 때, 그 결과는 예상과 다르게 true가 아니라 never 타입.
type IsNever = never extends never ? true : false
const never: IsNever = true
제네릭 타입이 아닐 때는 IsNever가 true 타입인데 왜 이것과는 다른 결과가 나올까? 여기서도 Distributive Conditional Type이 적용되기 때문. never 타입은 원시 타입이 아니라 공집합의 유니온 타입이기 때문에 제네릭에서 extends 절이 평가되지 않고 바로 never 타입으로 평가가 종료됨. 아래의 경우에는 Distributive Conditional Type가 적용되는 경우가 아니기 때문에 예상대로 true 타입이 됨.
Distributive Conditional Types
1. Distributive Conditional Types #
Checked Type
제네릭 타입에서 제약 조건을 검사하는 타입을 말하며, extends 키워드 앞에 나오는 타입을 말함. 예를 들어, A<T>에서는 string, B<T>에서는 { x: T }가 Checked Type이 됨.
Naked Type Parameter
제네릭 타입에서 타입 파라미터 각각을 말함. A<T>, B<T>에서는 T, C<T, U>에서는 T와 U가 Naked Type Parameter에 해당함.
위 조건에 해당하는 제네릭 타입이라면 MyUnion이 A | B | C의 유니온 타입일 때, D<MyUnion>의 결과는 아래와 같음.
A. Distributive Conditional Type 비활성화하기
타입 파라미터가 유니온 타입일 때 제네릭 타입이 유니온 멤버마다 적용되는 게 합리적이지만 경우에 따라서 원하지 않는 동작이 될 수 있음. 그런 경우에는 Distributive Conditional Type은 Checked Type이 Naked Type Parameter일 때 적용되므로 Checked Type이 Naked Type Parameter가 아니도록 변경해주면 됨.
B. 모든 객체의 key 타입 #
객체의 유니온 타입에 대해 모든 객체들의 key 값 타입을 얻고 싶을 때 단순하게 위와 같이 작성할 수 있지만 그 결과는 never 타입. 유니온 타입에 대한 keyof는 모든 객체들의 key 타입이 아니라 공통된 key 타입이기 때문. 공통돤 key 타입이 아니라 모든 key 타입을 얻고 싶다면 Distributive Conditional Type에서는 각각의 유니온 멤버들에 대해 Conditional Type이 적용된다는 점을 이용하는 방법이 있음.
C. Type Parameter로 never가 사용될 때 #
위와 같이 제네릭 타입의 기본 파라미터로 never 타입을 사용했을 때, 그 결과는 예상과 다르게 true가 아니라 never 타입.
제네릭 타입이 아닐 때는 IsNever가 true 타입인데 왜 이것과는 다른 결과가 나올까? 여기서도 Distributive Conditional Type이 적용되기 때문. never 타입은 원시 타입이 아니라 공집합의 유니온 타입이기 때문에 제네릭에서 extends 절이 평가되지 않고 바로 never 타입으로 평가가 종료됨. 아래의 경우에는 Distributive Conditional Type가 적용되는 경우가 아니기 때문에 예상대로 true 타입이 됨.
https://github.com/microsoft/TypeScript/issues/27418
Question / Suggestion: Behaviour of unknown in distributive conditional type. · Issue #27418 · microsoft/TypeScript
This isn't a direct suggestion, more a query that includes a proposed alternate approach (though I'm not sure I would even want the alternate). Search Terms distributive conditional type unknown Su...
github.com
'TypeScript' 카테고리의 다른 글