如何在打字稿中为 const 定义重载签名?

Posted

技术标签:

【中文标题】如何在打字稿中为 const 定义重载签名?【英文标题】:How to define overloaded signatures for a const in typescript? 【发布时间】:2020-08-05 10:27:10 【问题描述】:

我们可以在 typescript 中定义这样的重载函数:

function hello(name: number): void;
function hello(name: string): void;
function hello(name: number | string): void 
  // ...

我尝试将此函数定义为 const,例如:

const hello = (name: number | string): void => 
  // ...

但不确定如何在其上声明重载签名:

(name: number): void; 
(name: string): void;

请帮忙,谢谢

【问题讨论】:

【参考方案1】:

鉴于您的 overloaded 函数语句 hello,您可以通过在其类型中写入 type alias 并通过将鼠标悬停在您的 IDE 中使用 IntelliSense 进行检查来发现编译器自己推断的类型:

type Hello = typeof hello;
/* type Hello = 
    (name: number): void;
    (name: string): void;
 */

在这里你可以看到Hello被认为是一个对象类型,有两个call signatures,代表你在重载列表中声明的调用签名,顺序相同。

你也可以写一个等价的类型作为函数类型的intersection:

type AlsoHello = ((name: string) => void) & ((name: number) => void);

如果您有一个函数类型F1F2,那么F1 & F2 表示一个重载函数,其中F1 签名在F2 签名之前被检查。在(越来越过时的)TypeScript spec 中它说:

虽然A & B 等价于B & A 通常是正确的,但在确定调用和构造交叉点类型的签名时,构成类型的顺序可能很重要。

在其余答案中,我将使用 object-type-with-multiple-call-signatures 版本,而不是 intersection-of-arrow-function-signatures 版本。


无论如何,在这种特殊的情况下,两个签名都返回void,如果您使用上述类型annotate,您可以将hello 重写为const 而不会出错:

const helloConst: 
  (name: number): void;
  (name: string): void;
 = (name: number | string): void => 
  // ...

一般来说,一个警告......重载的函数语句没有像const 赋值那样严格检查。重载的函数语句允许实现签名的返回类型匹配调用签名的返回类型的联合,即使这不安全:

function goodbye(name: number): number;
function goodbye(name: string): string;
function goodbye(name: number | string): number | string 
  return typeof name === "number" ? name + 1 : name + "!";

上面没有编译器错误。 goodbye() 的实现返回number | string,你可以将typeof name === "number" 更改为typeof name !== "number",编译器仍然不会警告你。这通常被认为是一个特性,而不是一个错误。

但是现在如果你把它写成const,你会得到错误:

const goodbyeConst:  // error!
  (name: number): number;
  (name: string): string;
 = (name: number | string): number | string =>
    typeof name === "number" ? name + 1 : name + "!";
// Type '(name: string | number) => string | number' 
// is not assignable to 
// type ' (name: number): number; (name: string): string; '.

const 赋值检查更严格,编译器(正确地)抱怨您不能安全地将(name: string | number) => string | number 类型的函数视为((name: string) => string) & ((name: number) => number) 类型的函数。毕竟,实现总是可以返回满足实现签名但不匹配number调用签名的string

无论如何,在这种情况下进行的方法是使用type assertion 而不是注释:

const goodbyeConstAssert = ((name: number | string): number | string =>
  typeof name === "number" ? name + 1 : name + "!") as  // error!
    (name: number): number;
    (name: string): string;
  

编译没有错误。


好的,希望对您有所帮助;祝你好运!

Playground link to code

【讨论】:

【参考方案2】:

您可以使用可调用签名定义类型/接口并使用它来键入变量:

type Hello = 
    (name: number): void,
    (name: string): void,


const hello: Hello = (name: number | string): void => 
    // ...


hello(1); // ok
hello('1'); // ok

declare const wrong: string | number;
// @ts-expect-error
hello(wrong)

Playground

【讨论】:

以上是关于如何在打字稿中为 const 定义重载签名?的主要内容,如果未能解决你的问题,请参考以下文章

我如何在打字稿中为 Angular 5 设置 zindex

打字稿中的 baseUrl 和路径 - 如何

我将如何在打字稿中定义这个对象?

如何在打字稿中定义一个常量数组

如何在打字稿中定义敲除绑定处理程序?

如何在打字稿中正确导入自定义类型