Typescript 函数重载奇怪的行为

LGe*_*lis 5 typescript

我正在尝试了解 Typescript 中的函数重载如何工作,但我不明白。检查此代码片段 ( ts Playground )(仅使用一个声明以使其尽可能简单):

function foo(x: number): 42

function foo(x: number) {
   return x
}

const y = foo(23)
Run Code Online (Sandbox Code Playgroud)

有两件事不对劲:

  • 即使实际返回类型明显与重载签名不匹配,函数声明也不会出现 TS 错误
  • y的推断值为42

ts游乐场的截图

相反,如果我只是在函数实现上声明返回类型,则事情会按预期工作(或失败!):

function foo(x: number): 42 {
   return x // -> Type 'number' is not assignable to type '42'.(2322)
}

const y = foo(4)
Run Code Online (Sandbox Code Playgroud)

jpe*_*erl 3

让我们解决第一个问题:

\n
\n

即使实际返回类型明显与重载签名不匹配,函数声明也不会出现 TS 错误

\n
\n

如果你看一下这个文档(此后已弃用,但我想它仍然有效),他们说:

\n
\n

为了让编译器选择正确的类型检查,它遵循与底层 JavaScript 类似的过程。它查看重载列表,并继续进行第一个重载,尝试使用提供的参数调用该函数。如果找到匹配项,它就会选择该重载作为正确的重载。因此,习惯上按从最具体到最不具体的顺序对重载进行排序。

\n
\n

我的猜测是,当您编写实现时,它是相同的,对于您声明的每个重载,它将尝试用它来调用您的函数。我不知道这是否有道理。让我们看一个例子:

\n
function foo(x: number): 42 // ok since 42 is compatible with number (number is wider than 42)\n\nfunction foo(x: number) {\n   return x\n}\n\nconst y = foo(23)\n
Run Code Online (Sandbox Code Playgroud)\n

如果您获取签名,并尝试用它调用您的函数,那么它是“兼容的”。x 是一个数字,42 也是一个数字,我们实现的返回类型表明 x 是一个数字。

\n

如果我这样做:

\n
function foo(x: number): 42 // error: not compatible since 42 is not compatible with 32\n\nfunction foo(x: number) {\n   return 32\n}\n
Run Code Online (Sandbox Code Playgroud)\n

我收到一个错误。另一条道路:

\n
function foo(x: number): \'text1\'\nfunction foo(x: string): \'text2\'\n\nfunction foo(x: number | string) {\n  return x // why is there no error?\n}\n\n//y is "text1" (no, it\'s a number!!!)\nconst y = foo(42)\n\n\n//there is no error because if you take the signature, you can call your function with it -> x is a number so compatible with number | string and \'text1\' is compatible with number | string\n
Run Code Online (Sandbox Code Playgroud)\n

那么你可能会问为什么会这样呢?如果按照下面的方式来看的话,就更容易推理了。

\n

不是

\n
\n

我可以用我的函数实现来调用我的签名吗?

\n
\n

相反

\n
\n

如果我采用我的签名之一,我可以用它调用我的函数实现吗?

\n
\n

不要将重载视为函数实现必须满足的某种要求。您是否注意到错误出在重载上而不是函数实现上?这是必须与函数实现兼容的重载。

\n
\n

第二点:

\n
\n

y 的推断值为 42

\n
\n

如果将鼠标悬停在函数实现上,您将看到它只显示重载。

\n

该文档的最新版本说:

\n
\n

实现的签名从外部是不可见的。\n编写重载函数时,应该始终在函数的实现上方有两个或\n更多签名。

\n
\n

它们提供了人们常犯的一个错误:

\n
function fn(x: string): void;\nfunction fn() {\n  // ...\n}\n// Expected to be able to call with zero arguments\nfn();\nExpected 1 arguments, but got 0.\n
Run Code Online (Sandbox Code Playgroud)\n

实现本身不算是重载。

\n

所以调用函数时只能看到重载:

\n
const y = foo(23)\n
Run Code Online (Sandbox Code Playgroud)\n
\n

因此,考虑到这一点,明智地使用它们的重载。剩下的就是你的责任了。顺便说一句,在您的用例中,您根本不需要使用重载。仅当您至少拥有其中两个时才应使用它。

\n