首页 > 解决方案 > 在需要函数的地方,打字稿允许我传递具有不兼容的“应用”属性的对象

问题描述

目前使用 typescript 3.4.5strict启用模式...

背景故事

不幸的是,我刚刚遇到了打字稿无法保护我免受错误的情况。我试图弄清楚为什么打字稿未能捕捉到这个错误。

我正在为这样的函数编写类型声明:

function acceptVisitors (visitor) {
    visitor.apply(value);
}

精明的观察者可能会指出visitor's 的类型可以通过以下两种方式之一定义——作为函数,或者作为具有apply属性的对象:

type visitorType = (this: IValue) => void;
// or
type visitorType = {
    apply: (value: IValue) => void;
};

事实证明,在我的情况下是后者。添加类型声明后,我继续编写此错误代码:

// This is incorrect because it doesn't pass it as an argument.
// Rather, the `this` context is set to the value.
acceptVisitors((value: IValue) => { ... });

现在,令人费解的是,当我传递一个类型不兼容的函数时,Typescript 没有显示错误visitorType

简化示例

让我们将参数类型更改为字符串,然后遍历它。
我正在定义一个名为的类型func,它是一个需要字符串参数的函数。

type func = (param1: string) => void;

函数本质上是具有应用方法的可调用对象。

declare let f: func;
f.apply(undefined, ['str']);
// all good

现在是另一种类型——具有apply属性的对象。

type objectWithApplyProp = {
    apply: (param1: string) => void;
};

我们可以调用 apply 属性,但方式不同......

declare let o: objectWithApplyProp;
o.apply(undefined, ['str']); // Error: Expected 1 arguments, but got 2.

并且objectWithApplyProp有一个不适用的呼叫签名func

o.apply('str'); // ok
f.apply('str'); // Error: The 'this' context of type 'func' is not assignable to 
                // method's 'this' of type '(this: string) => void'

进一步的测试表明它f可以分配给o,但不是相反,这是有道理的……所有函数都是对象,但并非所有对象都是可调用的。

但是为什么被f认为可以分配给o?的类型objectWithApplyProp需要一个与特定类型apply匹配的值,但不匹配func

一个函数的apply签名应该可以从它的参数中推断出来,但打字稿似乎并没有推断出它。

因此,欢迎任何反馈。我错了,还是打字稿有限制?这是一个已知问题吗?谢谢

标签: typescript-typings

解决方案


因此,这是发生这种情况的技术原因,也是一种解决方法:

Typescript 的内置lib/es5.d.ts声明文件定义Function.apply了 type 的参数any。它也定义Function.prototype为`any。

interface Function {
    apply(this: Function, thisArg: any, argArray?: any): any;
    call(this: Function, thisArg: any, ...argArray: any[]): any;
    bind(this: Function, thisArg: any, ...argArray: any[]): any;
    toString(): string;
    prototype: any;
    readonly length: number;
    // Non-standard extensions
    arguments: any;
    caller: Function;
}

我猜所有的函数表达式都Function默认给出了类型。

因此,允许将函数分配给具有不兼容apply属性的对象,因为该函数没有apply基于内置Function类型的强类型方法。因此打字稿无法确定apply签名不同。

Typescript 3.2引入了CallableFunction,它在其apply声明中具有通用参数。但我还没有想出如何让它解决这个问题。

一种解决方法是定义更强的函数类型并手动将其分配给函数。解决方法有点乏味,但它有效。

interface func extends Function {
    (param1: string): void;
    // manually define `apply
    apply<T, R> (thisArg: T, args: [string]): R;
}
interface objectWithApplyProp { // unchanged
    apply: (param1: string) => void;
}
// Now we have proper errors here:
o = f;
// Type 'func' is not assignable to type 'objectWithApplyProp'.
//  Types of property 'apply' are incompatible.
//    Type '<T, R>(thisArg: T, args: [string]) => R' is not assignable to type '(param1: string) => void'. ts(2322)
f = o;
// Type 'objectWithApplyProp' is missing the following properties from type 'func': call, bind, prototype, length, and 2 more. ts(2740)

推荐阅读