Suppose there is such a function, how would you declare its type?

function add(a,b){
    return a+b;
}

add function may have two situations:

  1. The parameter a、b is of number , and the return value is of type number
  2. The parameter a、b is of string , and the return value is of type string

Can it be solved by using function overloading?

First, you may implement the overload declaration add

function add(a: string, b: string): string;
function add(a: number, b: number): number;
function add(a: any, b: any) {
    return a + b;
}
add(1, 2); //function add(a: number, b: number): number
add('x', 'y'); //function add(a: string, b: string): string

This method is obviously good, but there is a small problem: when the number|string parameter type is 06176873cbb325, an unexpected type error will occur.

let a:string|number;
add(a,a);
/*
  第 1 个重载(共 2 个),“(a: string, b: string): string”,出现以下错误。
    类型“string | number”的参数不能赋给类型“string”的参数。
      不能将类型“number”分配给类型“string”。
  第 2 个重载(共 2 个),“(a: number, b: number): number”,出现以下错误。
    类型“string | number”的参数不能赋给类型“number”的参数。
      不能将类型“string”分配给类型“number”。
*/

This is because when we use function overloading, TypeScript uses these overloads to compare one by one until the appropriate type overload is matched. But obviously, we declare the variable type two overloaded in, number and string are associated with number|string does not match, so there are a type of error.

How about using generics?

Then, you might also think of using generics to declare types in order to build a common pattern.

function add<T extends number | string>(a: T, b: T): T;
function add(a: any, b: any) {
    return a + b;
}
const a:number = 0;
const b:string='str';
add(a, a);//function add<number>(a: number, b: number): number
add(b, b);//function add<string>(a: string, b: string): string

This method looks good, but also has a small problem: When an incoming literal type of parameters, the type of the parameter will be considered and the parameters same value type. E.g:

add(1,2);//function add<1 | 2>(a: 1 | 2, b: 1 | 2): 1 | 2

Parameters 1 of type is 1 , and non number , just as we declare type T=1|2 ; as a function of the type of declaration generic T necessary to meet the first argument 1 , but also to meet the second argument 2 , So the type of T becomes 1|2 ; similarly, if the incoming variable a、b does not have explicit type declaration number and string , this problem will also occur.

Condition type might be better

In fact, using conditional types when such functions need to be overloaded may have better results.

function add<T extends number | string>(
    a: T,
    b: T
): T extends number ? number : string;
function add(a: any, b: any) {
    return a + b;
}
add(1,2);//function add<1 | 2>(a: 1 | 2, b: 1 | 2): number
let a:number|string;
add(a, a);//function add<string | number>(a: string | number, b: string | number): string | number

In this way, the type of the parameter is first obtained through the generic method, and then the condition type is used for judgment, so as to obtain the correct type.

This method is more complicated to write than the previous two methods, but the type will be more accurate. When a function requires type overloading, consider using conditional types to implement it.


forceddd
271 声望912 粉丝

一名前端爱好者。