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

Fre*_*ind 19 overloading typing typescript

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

function hello(name: number): void;
function hello(name: string): void;
function hello(name: number | string): void {
  // ...
}
Run Code Online (Sandbox Code Playgroud)

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

const hello = (name: number | string): void => {
  // ...
}
Run Code Online (Sandbox Code Playgroud)

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

(name: number): void; 
(name: string): void;
Run Code Online (Sandbox Code Playgroud)

请帮忙,谢谢

jca*_*alz 15

给定您的重载函数语句,您可以通过为其类型编写类型别名并通过将鼠标悬停在 IDE 中的 IntelliSense 上进行检查来发现hello编译器自行推断的类型:

type Hello = typeof hello;
/* type Hello = {
    (name: number): void;
    (name: string): void;
} */
Run Code Online (Sandbox Code Playgroud)

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

您还可以编写等效类型作为函数类型的交集

type AlsoHello = ((name: string) => void) & ((name: number) => void);
Run Code Online (Sandbox Code Playgroud)

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

虽然通常情况下A & B等于B & A,但在确定交集类型的调用和构造签名时,组成类型的顺序可能很重要。

在答案的其余部分中,我将使用带有多个调用签名的对象类型版本,而不是箭头函数签名的交集版本。


无论如何,在两个签名都返回的特殊情况下,如果您使用上述类型对其进行注释void,则可以重写hello为 aconst而不会出现错误:

const helloConst: {
  (name: number): void;
  (name: string): void;
} = (name: number | string): void => {
  // ...
}
Run Code Online (Sandbox Code Playgroud)

不过,一般来说需要注意的是……重载函数语句的检查并不像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 + "!";
}
Run Code Online (Sandbox Code Playgroud)

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

但现在如果你把它写成 a 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; }'.
Run Code Online (Sandbox Code Playgroud)

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

无论如何,在这种情况下继续进行的方法是使用类型断言而不是注释:

const goodbyeConstAssert = ((name: number | string): number | string =>
  typeof name === "number" ? name + 1 : name + "!") as { // no error
    (name: number): number;
    (name: string): string;
  }
Run Code Online (Sandbox Code Playgroud)

编译没有错误。


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

Playground 代码链接


Ale*_* L. 9

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

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)
Run Code Online (Sandbox Code Playgroud)

操场