유용한 타입 만들기
판단하는 타입만들기
타입스크립트를 작성할 때는 특정 타입이 무슨 타입인지 판단할 수 있어야 한다.
그래야 타입을 컨디셔널 타입으로 제거할 수 도 있고, 그 타입만 추릴 수 있다.
IsNever
T에 never를 넣을 때 분배 법칙이 일어난것을 막기 위해 배열로 감싸는것을 확인
never를 유니온으로 생각, 제너릭과 유니온이 같이 쓰이면 분배 법칙이 발생
never에 분배 법칙이 발생하면 never가 된다.
type IsNever<T> = [T] extends [never] ? true : false
IsAny
string과 number는 겹치지 않아 extends 할수가 없다.
또한 number & T는 number의 부분집합이므로 더욱 string과 겹치지 않음
But, T가 any라면 이야기 달라짐
any라면 number & T(any) 는 any가 되고 string 은 any를 extends 할 수 있게됨
type IsAny<T> = string extends (number & T) ? true : false
IsArray
복잡한 이유
isArray<never>가 never가 되는것을 막기 위해 IsNever<T> extends true 필요
isArray<any>가 boolean이 되는것을 막기 위해 IsAny<T> extends true가 필요
isArray<readonly []>가 false가 되는것을 막기 위해 T extends readonly unknown[]이 필요
type IsArray<T> = IsNever<T> extends true
? false
: T extends readonly unknown[]
? IsAny<T> extends true
? false
: true
: false;
IsTuple
배열과 튜플의 차이는 튜플의 길이가 고정되어 있다는 것
튜플이 아닌 배열은 length가 number
튜플은 1,2,3 같은 고정된 개별 숫자가 됨
그래서 number extends T["length"]가 false여야 되는것이 핵심
any 판별 조건도 여기서 같이 걸러짐
type IsTuple<T> = IsNever<T> extends true
? false
: T extends readonly unknown[]
? number extends T["length"]
? false
: true
: false;
IsUnion
U = T
,T extends T
는 무슨 의미인가?T extends T는 항상 true 이지만 분배법칙을 만들기 위해 사용함
유니온의 경우 컨디셔널 타입 제너릭과 만나면 분배법칙이 발생
T가 string | number 인경우 T extends T 는 string | number extends string | number 가 아니라 (string extends string | number) | (number extends string | number)가 된다.
[U] extends [T] 는 분배법칙이 일어나지 않게 해서 [string | number] extends [string] 또는 [string | number] extends [number]가 된다.
최종적으로 false가 되어 union 타입인 IsUnion<string | number>는 true가 된다.
type IsUnion<T, U = T> = IsNever<T> extends true
? false
: T extends T
? [U] extends [T]
? false
: true
: false;
집합 관련 타입 만들기
타입스크립트의 타입은 집합으로 생각해도 될 정도로 집합의 원리를 충실히 따름
전체 집합은
unknown
, 공집합은never
합집합
|
, 교집합&
차집합
A가 { name: string, age: number}, B는 { name: string, married: boolean } 인 경우 둘을 차집합 (A-B)하면 { age: number }가 나와야하고 (B-A)라면 { married: boolean } 이 나와야 한다.
type Diff<A, B> = Omit<A & B, keyof B>;
type R1 = Diff< {name: string, age: number}, {name: string, married: boolean}>;
// { age: number }
Omit
특정 객체에서 지정한 속성을 제거하는 타입
Diff 응용
Diff 타입을 조금 더 응용하면 대칭차집합도 찾아 낼 수 있다. (서로 겹치지 않는 타입 모두 합쳐놓은 것) 즉, 합집합에서 교집합을 뺀 것이라고도 볼 수 있음
type SymDiff<A,B> = Omit<A & B, keyof (A | B)>;
type R2 = SymDiff< {name: string, age: number}, {name: string, married: boolean}>;
// { age: number, married: boolean }
Exclude
어떤 타입 (A | B) 에서 다른 타입 (A & B)를 제거하는 타입
type IsSubset<A, B> = A extends B ? true : false;
type R1 = IsSubset<string, string | number>; // true
type R2 = IsSubset<{ name: string, age: number }, { name: string }>; // true
type R3 = IsSubset<symbol, unknown>; // true
Equal
두 타입이 동일하다는 판단
타입도 집합이므로 A가 B의 부분집합이고 B도 A의 부분집합이면 A와 B는 서로 동일하다는 듯
type Equal<A, B> = A extends B ? B extends A ? true : false : false;
위 코드는 허점이 있음, true로 예상했으나 그렇지 않은 사레들이 존재
type R1 = Equala<boolean, true | false> // boolean
type R2 = Equal<never, never> // never
유니온이 일어나는 분배법칙 때문 (never도 유니온이라 생각)
분배법칙이 일어나지 않도록 배열로 감싸기
type Equal<A, B> = [A] extends [B] ? [B] extends [A] ? true : false : false
단 위 Equal 타입은 any를 구분할 수 없다.
type Equal2<X, Y>
= (<T>() => T extends X ? 1 : 2) extends (<T>() => T extends Y ? 1 : 2)
? true
: false
위 Equal2 타입은 any와 다른 타입을 구별 할 수 있지만 인터섹션은 구분 못함
Equal2<any, unknown> 의 경우 extends 를 false로 만드는 T가 없음에도 false가 된다.
type R5 = Equal2<any, 1>// false
type R6 = Equal2<{x:1} & {y:2}, {x:1, y:2}>; //false
type R7 = Equal2<any, unknown>; // false
NotEqual
Equal 타입의 결과를 반대로 적용
type NotEqual<X, Y> = Equal<X, Y> extends true ? false : true;
Last updated