0

如果我在接口内定义一个方法,然后稍后实现它,则如果接口使用方法签名(它适用于属性签名),则不会强制执行其参数类型。

例子

它看起来很像一个错误,是吗?是故意的吗?有没有办法在不将我们所有的函数更改为属性签名的情况下解决它?

它与这个问题不同,因为在这个问题中,接口的方法接受一些参数并且实现可以在没有它的情况下工作(这是合乎逻辑的)。但是这里的实现期望接收接口中不存在的扩展参数

4

1 回答 1

1

这是故意的。关于函数参数和方差的一些背景知识(如果您已经知道,可以跳过):

如果我要求一个接受 a 的函数string,而你给我一个接受 a 的函数unknown,我会很高兴。我可以安全地传递该函数 astring并且它会接受它,因为它string可以分配给unknown. 这是逆变,因为可分配性切换的方向:string可分配给unknown,因此(x: unknown) => void可分配给(x: string) => void

将此与covariance进行比较,其中可分配性的方向保持不变:"foo"可分配给string,因此(x: "foo") => void可分配给(x: string) => void? 不,这不对。如果我要求一个接受 a 的函数string,而你给我一个只接受字符串字面量的函数,"foo"如果我使用它,我可能会不高兴。如果我传递的函数 astring不是"foo",那么该函数将不接受它。

因此,以逆变方式检查函数参数是类型安全的,而以协变方式检查它们不是类型安全的。那么 TypeScript 是做什么的呢?好吧,在 TypeScript 2.6 之前,编译器实际上同时允许. 也就是说,它以双变量方式检查所有函数和方法参数

为什么会这样做?其中一个问题与修改对象的方法有关。考虑链接的常见问题解答中的示例:push()数组方法。我们通常希望 astring[]可分配给unknown[]。如果我只从这样的数组中读取,这是非常安全的。我要一个unknown[];你给我一个string[];我从中读取,提取unknown值(恰好是string对象,但这很好)。但是写入数组是不安全的。如果我打电话array.push(123),如果是的话应该没问题,但如果arrayunknown[]的话就很糟糕了string[]。如果仅以逆变方式检查参数 to push(),这将强制执行安全性,不允许string[]分配给unknown[]. 但是,正如他们在常见问题解答中所说,那将是“令人难以置信的烦人”。相反,它们允许不安全但不方便的约定,即允许函数和方法参数以两种方式变化。


回到这个问题的答案:幸运的是,对于我们这些喜欢更多类型安全性的人来说,TypeScript 2.6 引入了一个--strictFunctionTypes标志,当启用该标志时,会导致函数参数类型仅以逆变方式而不是双变量方式进行检查。

但是对于Array经常协变使用的其他内置类仍然存在这个问题。为了防止--strictFunctionTypes“令人难以置信的烦人”,权衡是函数参数被逆变检查,但方法参数仍然被双变量检查。所以从 TS2.6 开始,编译器在涉及类型检查参数时关心方法签名和函数签名之间的区别。

因此使用以下界面:

interface Example {
    func: (x: string) => void;
    method(x: string): void;
}

您会得到以下行为(在--strictFunctionTypes启用的 TS2.6+ 中):

const contravariant: Example = {
    func(x: unknown) { }, // okay!
    method: (x: unknown) => { } // okay!
}

const covariant: Example = {
    func(x: "foo") { }, // error!
    method: (x: "foo") => { } // okay!
}

注意在contravariantand中covariant实现为方法,实现函数值属性,但还是按照接口中的规则进行检查:有函数签名,严格检查,有方法签名,检查松散地。funcmethodExamplefuncmethod


那么,有没有办法在不将所有函数更改为属性签名的情况下解决它?大概没什么大不了的。您可能会尝试以编程方式转换签名,如下所示:

type MethodsToFunctionProps<T> = {
    [K in keyof T]: T[K] extends (...args: infer A) => infer R ? (...args: A) => R : T[K]
}

在这种情况下有效:

const covariantFixed: MethodsToFunctionProps<Example> = {
    func(x: "foo") { }, // error!
    method: (x: "foo") => { } // error!
}

但可能会有边缘情况,所以我不知道这对你是否值得。


无论如何,希望有所帮助;祝你好运!

链接到代码

于 2019-12-12T20:29:31.883 回答