[TypeScript] 섹션9. 조건부 타입

jaehoon ahn·2025년 2월 19일

TypeScript

목록 보기
13/14
post-thumbnail

조건부 타입

⇒ 삼항 연산자에 따라 타입을 결정하는 문법

예시1

// 예시
type A = number extends string ? string : number;

예시2

// 예시
type ObjA = {
  a: number;
};
type ObjB = {
  a: number;
  b: number;
};

type B = ObjB extends ObjA ? number : string;
// -> A가 슈퍼타입, 조건이 참이되어 B는 number 타입이 된다.

제네릭과 조건부 타입

type StringnumberSwitch<T> = T extends number ? string : number;
// T가 number면 string, string이면 number 타입으로 할당 된다.
let varA: StringnumberSwitch<number>; // string 타입
let varB: StringnumberSwitch<string>; // number 타입

오버로드 시그니쳐

에러 발생 코드

function removeSpaces(text: string | undefined | null) {
  if (typeof text === "string") return text.replaceAll(" ", "");
  else return undefined;
}
let result = removeSpaces("hi im jeno");
result.toUpperCase(); // string or undefined가 될 수 있으므로 에러 발생

에러 해결

function removeSpaces<T>(text: T): T extends string ? string : undefined;
function removeSpaces(text: any) {
  if (typeof text === "string") {
    return text.replaceAll(" ", "");
  } else {
    return undefined;
  }
}
let result = removeSpaces("hi im jeno"); // string으로 추론되며 오류 해결
result.toUpperCase();

let result2 = removeSpaces(undefined);

분산적인 조건부 타입

type StringnumberSwitch<T> = T extends number ? string : number;

let c: StringnumberSwitch<number | string>;
// 유니온 타입을 할당하면 분산적인 조건부 타입으로 업그레이드됌
// 한꺼번에 두 타입이 들어가는게 아니라, 한번은 number, 한번은 stirng으로 
// 분리되어서 들어가게 된다.
// StringnumberSwitch<number> -> string타입
// StringnumberSwitch<string> -> number타입
// 분리된 결과들을 각각 유니온으로 묶어준다. => string | number 타입으로 된다.
let d: StringnumberSwitch<boolean | number | string>;
// 1단계
// StringnumberSwitch<boolean>
// StringnumberSwitch<number>
// StringnumberSwitch<string>

// 2단계
// number |
// string |
// number

// 결과
// number | string으로 나오게 됌

실용적인 예제

type Exclude<T, U> = T extends U ? never : T;
// 타입 변수 T가 U의 서브타입이라면 never, 아니면 T
type A = Exclude<number | string | boolean, string>;
// 1단계
// Exclude<number, string> |
// Exclude<string, string> |
// Exclude<boolean, string>

// 2단계
// number |
// never |
// boolean

// 결과
// number | never | boolean
// 유니온 타입에 never가 있으면 사라진다.
// 유니온 타입은 타입들간의 합집합 타입을 만드는 것, never타입은 공집합 타입
// 공집합과 다른 집합을 합집합 한다는 것은 결국 원본 => number | never => number
// 따라서, A는 number | boolean 타입

예시

type Extract<T, U> = T extends U ? T : never;

type B = Extract<number | string | boolean, string>;
// 1단계
// Extract<number, string>
// Extract<string, string>
// Extract<boolean, string>

// 2단계
// never
// string
// never

infer - 조건부 타입 내에서 타입 추론하기

⇒ infer → inference(추론)

반환값의 타입만 가져오기

type FuncA = () => string;
type FuncB = () => number;
type ReturnType<T> = T extends () => infer R ? R : never;
type A = ReturnType<FuncA>; // string 타입
// infer R은 R로 보면 된다. -> 타입 변수로
// infer R타입은 () => string을 참으로 만드는 타입을 추론하도록 동작함
// () => string이 () => R의 서브타입이 되려면 R타입은 string 이 되서 R이 string이 된다.

type B = ReturnType<FuncB>; // number 타입
// type B 또한 A와 같이 동작한다.

type C = ReturnType<number>; // never 타입
// () => infer R은 number 타입의 슈퍼타입이 될 수 없다. 추론 불가

예제

type PromiseUnpack<T> = T extends Promise<infer R> ? R : never;
// 1. T는 프로미스 타입이어야 한다.
// 2. 프로미스 타입의 결과값 타입을 반환해야 한다.

type PromiseA = PromiseUnpack<Promise<number>>;
// number타입
type PromiseB = PromiseUnpack<Promise<string>>;
// string타입

0개의 댓글