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

How to define overloaded signatures for a const in typescript?

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

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

我试图将此函数定义为常量,例如:

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

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

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

请帮忙,谢谢

您可以使用可调用签名定义 type/interface 并使用它来键入变量:

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

将您的 overloaded function statement hello, you can discover the type that the compiler infers for it yourself by writing a 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 它说:

While it is generally true that A & B is equivalent to B & A, the order of the constituent types may matter when determining the call and construct signatures of the intersection type.

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


无论如何,在这种 特殊的 情况下,两个签名 return void,您可以将 hello 重写为 const如果你 annotate 上面的类型没有错误:

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

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

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() returns 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) 的函数.毕竟,实现总是可以 return string 满足实现签名但不匹配 number 调用签名。

无论如何,在这种情况下继续进行的方法是使用 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