乐趣区

关于typescript:请别误用-TypeScript-重载函数类型

TypeScript 容许定义重载函数类型,采多间断多个重载申明 + 一个函数实现的形式来实现。比方

function func(n: number): void;
function func(prefix: string, n: number): void;
function func(first: string | number, n?: number): void {if (typeof first === "string") {console.log(`${first}-${n}`);
    } else {console.log(`number-${first + 10}`);
    }
}

示例中的 func() 函数有两个重载:

  • (number) => void
  • (string, number) => void

它的实现局部,参数和返回值申明要兼容所有重载,所以第一个参数可能是 number 或者 stringfirst: string | number;而第二个参数有可能是 number 或者没有,也就是 n?: number

重载函数的申明能够用函数的接口申明形式来定义。下面的重载函数类型能够如下定义:

interface Func {(n: number): void;
    (prefix: string,  n: number): void;
}

测验一下:

const fn: Func = func;

以上是“序”!


当初,忘掉 func(),咱们有别离定义的两个函数 func1()func2()

function func1(n: number): void {console.log(`number-${n + 10}`);
}

function func2(prefix: string, n: number): void {console.log(`${prefix}-${n}`);
}

以及有一个 render() 函数,心愿依据传入的函数来渲染输入:

function render(fn: Func): void {if (fn.length === 2) {fn("hello", 9527);
    } else {fn(9527);
    }
}

到当初为止,所有都还没有什么问题。紧接着就是测试 render()

render(func1);
render(func2);

问题来了,不论是 func1 还是 func2,都不能正确匹配 render() 的参数类型!!!

这里有一个不少人对重载函数类型了解的误区,认为如果函数 f 合乎重载函数类型 Fn 的某个重载签名,那么它就应该能够当作这个重载类型来应用。

其实不然,如果一个函数要匹配重载函数类型,那么它肯定也是一个重载函数(或者兼容所有重载类型的一个函数)。拿下面的例子来说,如果传入 func1render() 运行时确实是能够精确地进入到 else 分支,并胜利调用 fn(9527);传入 func2 也能精确进入 if 分支并胜利调用 fn("hello", 9527)。然而 ——

这些事件都是在运行时,由 JavaScript 干的。而 TypeScript 的编译器,在动态剖析的时候发现 render() 的参数 fn 须要可能兼容 fn(string, number) 的调用,以及 fn(number) 的调用,不论是 func1() 还是 func2() 都不具备全副条件。

所以下面示例中,传入 render() 的参数只能是重载函数 func 而不能是 func1 或者 func2

那如果想达到原始目标该怎么办呢?

假如有类型 Func1Func2 别离是 func1()func2() 的类型,这个 render() 函数应该这么申明:

function render(fn: Func1 | Func2) {...}

不过这么一来,render() 函数原来的函数体就行不通了,因为 fn 是两种类型中的一种,但在调用时并不能确定是哪种。咱们须要写一个类型断言函数来帮忙 TypeScript 推断。残缺示例如下:

type Func1 = (n: number) => void;
type Func2 = (prefix: string, n: number) => void;

function isFunc2(fn: Func1 | Func2): fn is Func2 {return fn.length === 2;}

function render(fn: Func1 | Func2): void {if (isFunc2(fn)) {fn("hello", 9527);
    } else {fn(9527);
    }
}

render(func1);  // number-9527
render(func2);  // hello-9527

最初总结 & 强调一下:重载函数类型 和参加重载类型的各 函数类型的联结 是齐全不同的两种类型,请留神区别,不要误用。

退出移动版