为什么重载字符串参数时必须在实现之前有一个通用的类型声明?

Why must there be a general type declaration before the implementation when overloading string parameters?

TypeScript 支持重载字符串参数,以便 return any 的方法在使用某些参数调用时可以正确键入。

规范中有两个地方对此进行了定义: https://github.com/Microsoft/TypeScript/blob/master/doc/spec.md#1.8 https://github.com/Microsoft/TypeScript/blob/master/doc/spec.md#3.9.2.4

然而,让这些正常工作可能很困难。这是一个具有通用 get 的示例 class。当您将字符串 "a""b" 传递给此函数时,我想提供特定类型,并且在所有其他情况下 return 类型为 any.

我包含两个专用签名,然后提供通用签名,然后提供具有通用签名的实现。下面的代码正确地报告了对 xy 的前两个赋值的错误,但是如果我删除通用签名 (get(name: string): any),我会得到错误:Argument of type '"c"' is not assignable to parameter of type '"b"'. 为什么除了实现上的签名外,是否还需要通用签名?

export default class Example {
  contents: any
  get(name: "a"): number
  get(name: "b"): string
  // Why is this required???
  get(name: string): any
  get(name: string): any { return this.contents[name] }
}


let w = new Example()

// Expected errors
// Type 'number' is not assignable to type 'string'.
let x: string = w.get("a")
// Type 'string' is not assignable to type 'number'.
let y: number = w.get("b")

// I get an error here only if I remove the general signature before the
// implementation of get.
// Argument of type '"c"' is not assignable to parameter of type '"b"'.
let z: string[] = w.get("c")

注意section 6.2 of the spec, "Function overloads"

的最后一行

Note that the signature of the actual function implementation is not included in the type.

这是有道理的,因为实现的签名需要匹配所有可能的签名,如果它包含在最终签名中,这可能会导致比我们实际想要的更通用的签名。例如:

function foo(type: "number", arg: number)
function foo(type: "string", arg: string)
function foo(type: "number" | "string", arg: number | string) {
    // impl here
}

由于之前的签名,实现的签名是必要的,但如果它包含在最终签名中,则将允许以下内容,尽管这正是我们想要防止的:

foo("number", "string param")