해당 포스트는 infa - 타입스크립트 - 조건부 타입 완벽 이해하기에서 모든 내용을 읽고 작성하는 글이기 때문에 해당 포스트를 읽는 것을 더 추천드립니다.
🕹️ 조건부 타입 사용 방법
조건부 타입을 보면 삼항 연산자와 거의 동일하게 생겼습니다.
동작도 비슷하지만 타입에서 쓰고 extends
키워드를 추가적으로 사용한다는 점과 조금 특별하게 동작하는 부분이 있어서 작성해보려고 합니다.
일반적이지는 않지만 (1)
과 같이 사용할 수 있습니다.
1
2
// (1)
type MyType = string | number extends string ? "a" : "b"; // "b"
일반적으로는 제네릭과 같이 사용((2)
)합니다.
1
2
3
4
5
// (2)
type MyGeneric<T> = T extends string ? "a" : "b";
// (3)
type MyType = MyGeneric<string | number>; // "a" | "b"
📄 조건부 타입의 특징
위 예시의 결과를 보면 이상하게 느껴지는 부분이 있습니다.
(1)
과 (3)
를 보면 결과적으로는 둘 다 string | number extends string ? "a" : "b";
처럼 사용한 것인데 타입이 다릅니다.
이 결과가 다른 이유에 대해서 자세하게 살펴보겠습니다.
0️⃣ 조건부 타입 + 유니온
제네릭을 거치지 않으면 모든 값이 extends string
에 만족하는지 검사하고 하나의 결과를 반환합니다.
1
2
type MyType1 = "a" | "1" | 1 extends string ? "a" : "b"; // "b"
type MyType2 = "a" | "b" | string extends string ? "a" : "b"; // "a"
1️⃣ 조건부 타입 + 제네릭
제네릭을 사용하면 전달한 값들 각각에 extends string
를 적용하고 그 결과들을 유니온으로 반환합니다.
(4)
에서는 string
과 number
둘 다 있으니 참과 거짓의 유니온인 "a" | "b"
가 나오게 된 것 입니다.
(5)
는 string
만 있기 때문에 참인 것의 유니온인 "a"
만 나오게 됐습니다.
1
2
3
4
5
6
type MyGeneric<T> = T extends string ? "a" : "b";
// (4)
type MyType1 = MyGeneric<"a" | "1" | 1>; // "a" | "b"
// (5)
type MyType2 = MyGeneric<"a" | "b" | string>; // "a"
2️⃣ 조건부 타입과 제네릭과 never 사용
조건부 타입에 never
를 사용하면 그 경우는 유니온에서 제외됩니다.
1
2
3
type MyGeneric<T> = T extends string ? T : never;
type MyType = MyGeneric<"a" | "1" | true | null | 1>; // "a" | "1"
🐣 Exclude 구현하기
Exclude<T, U>
는T
에서U
를 제외한 나머지를 얻는 유틸리티 타입입니다.
여태까지의 내용을 응용하면 typescript utility
중 하나인 Exclude<T, U>
를 직접 구현할 수 있습니다.
1
2
3
4
5
// T에 U가 포함되면 제외 아니면 T
type MyExclude<T, U> = T extends U ? never : T;
type MyType1 = Exclude<"a" | "b" | "c" | "d", "a" | "b">; // "c" | "d"
type MyType2 = MyExclude<"a" | "b" | "c" | "d", "a" | "b">; // "c" | "d"