乐趣区

关于前端:使用条件类型实现TypeScript中的函数重载

假如有这样的一个函数,你会怎么来申明他的类型呢?

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

add函数可能有两种状况:

  1. 参数 a、bnumber类型,返回值为 number 类型
  2. 参数 a、bstring类型,返回值为 string 类型

应用函数重载能解决吗?

首先,你可能会通过申明多个函数类型,来实现对 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

这种办法显然不错,然而有一个小问题:当传入的参数类型是 number|string 时,会呈现意想不到的类型谬误。

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”。*/

这是因为当咱们应用函数重载时,TypeScript 是应用这些重载来一一比对的,直到匹配到适合的类型重载。然而显然,咱们申明的两种重载中的变量类型,numberstring 都与 number|string 不匹配,所以呈现了类型谬误。

应用泛型怎么样呢?

而后,你还可能会想到应用泛型来申明类型,以便构建一种通用的模式。

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

这个办法看起来也很好,然而同样有一个小问题:当传入字面量类型的参数时,该参数的类型会被认为和参数的 值雷同 的类型。例如:

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

参数 1type1,而number,就如同咱们申明了 type T=1|2;一样,函数类型申明中的 泛型 T 既要满足第一个参数 1,又要满足第二个参数2,所以 T 的类型成为了1|2;同样的,如果传入的变量a、b 没有 显式 的类型申明 numberstring,也会呈现这个问题。

条件类型可能会更好

其实,在这种函数须要重载的时候应用条件类型,可能会有更好的成果。

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

这种形式是先通过泛型的办法获取到参数的类型,在应用条件类型进行判断,从而得出正确的类型。

这种办法相较于前两种办法写起来更简单,然而类型会更加精确。在函数须要类型重载时,无妨考虑一下应用条件类型来实现。

退出移动版