간단하게 number
와 string
타입의 매개변수를 받아 두개를 더하는 함수를 생각해보자
호기롭게 아래와 같이 작성해본다.
function double(input: string|number): string|number {
return input+input // type error
}
그리고 나선 input + input
부분에서 타입에러가 나는것을 볼 수 있다.
아래는 선언이 틀린 것은 아니지만 함수로 사용시 모호한 부분이 생긴다.
function double(input: string|number): string|number
function double(input: any): any {
return input+input
}
const a = double(1) // string|number
const b = double('1') // string|number
아래와 같이 제네릭으로 해결해보는 시도는 좋았지만, 너무 과하다. 리터럴 타입이 인자로 왔을때 기대하지 못한 값이 리턴타입이 된다
function double<T extends string|number>(input: T): T
function double(input: any): any {
return input+input
}
const a = double(1) // number
const b = double('1') // '1'
아래와 깉이 타입선언을 분리하면 구체적으로 사용할 수 있다.
function double(input:string):string
function double(input:number):number
function double(input: any): any {
return input + input
}
string
이나 number
값으로는 잘 동작하지만, 유니온 타입관련해서 문제가 발생한다.
function f(input: string|number) {
return double(input) // type error
}
아래처럼 해결할 수도 있다.
function double(input:string):string
function double(input:number):number
function double(input:string|number):string|number
function double(input: any): any {
return input + input
}
가장 좋은 해결책은 조건부 타입을 사용하는 것이다.
function double<T extends string|number>(input: T): T extends string ? string : number
function double(input:any):any {
return input + input
}
유니온에 조건부 타입을 적용하면, 조건부 타임의 유니온으로 분리된다
(number|string) extends string ? string : number
-> (number extends string ? string : number) | (string extends string ? string : number)
-> number | string
- 오버로딩 타입이 작성하기는 쉽지만, 조건부 타입은 개별타입의 유니온으로 일반화하기 때문에 타입이 더 정확해진다.
- 타입 오버로딩이 필요한 경우에 조건부 타입을 사용해서 개선할 수 있을 지 검토하자.