공부

[이펙티브TS] 오버로딩보다 조건부 타입 사용하기(아이템 50)

jaeeedev 2023. 8. 12. 21:13

 

function double(x) {
  return x + x;
}

double 함수에는 string이나 number가 들어올 수 있다.

function double(x: number | string): number | string;
function double(x: any) {
  return x + x;
}
const num = double(12); // number|string
const str = double("x"); // number|string

이런 결과 나옴. 타입이 아직 포괄적이다...

제네릭을 사용하면?

function double<T extends number | string>(x: T): T;
function double(x: any) {
  return x + x;
}
const num = double(12); //  12
const str = double("x"); // "x"

타입이 과하게 구체적이다. 그리고 str은 "xx"를 반환하기 때문에 의미도 모호하다.

여러개로 타입을 분리하면?

function double(x: number): number;
function double(x: string): string;
function double(x: any) {
  return x + x;
}

const num = double(12); // number
const str = double("x"); // string

잘 되는것 같지만... 유니온 타입 에서 문제 발생함

function f(x: number | string) {
  return double(x);
} // Argument of type 'string | number' is not assignable to parameter of type 'string'. 대충 이런 오류 나옴

타입스크립트는 오버로딩 타입 중 일치하는 타입을 찾을 때 까지 검색하는데 마지막까지 string|number 타입과 일치하는 타입이 없어서 오류 발생 (string이 string|number보다 정확한 타입이니까 할당 불가능)

조건부 타입 사용하기

function double<T extends number | string>(
  x: T
): T extends string ? string : number;
function double(x: any) {
  return x + x;
}

자바스크립트의 삼항 연산자처럼 사용하면 됨.

const num = double(12); //  number
const str = double("x"); // string

앞선 제네릭 예제와 유사하지만 반환 타입이 더 정교하다.

function (x: string | number) {
    return double(x);
}

유니온 타입일때도 문제없이 동작한다.

T가 number|string일 때 조건부 타입의 해석 과정

(number | string) extends string ? string: number

->

(number extends string ? string : number) | (string extends string ? string : number)

->

number | string

오버로딩 타입이 작성은 쉽지만 조건부 타입이 더 정확하다. 오버로딩 타입을 사용 중이라면 조건부 타입으로 개선을 고려해 볼 수 있다.