当前在启用strict
模式的情况下使用打字稿 3.4.5 ...
不幸的是,我只是遇到打字稿无法保护我免受自己的错误困扰的情况。我试图弄清楚为什么打字稿无法捕获此错误。
我正在为这样的函数编写类型声明:
function acceptVisitors (visitor) {
visitor.apply(value);
}
敏锐的观察者可能会指出,visitor
的类型可以通过以下两种方式之一进行定义-作为函数或具有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) => { ... });
现在,令人困惑的是,当我传递一个类型与visitorType
不兼容的函数时,Typescript没有显示错误。
让我们将参数类型更改为字符串,然后逐步遍历。
我正在定义一个名为func
的类型,该类型需要一个字符串参数。
type func = (param1: string) => void;
函数本质上是可调用对象,也具有apply方法。
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
签名,但是打字稿似乎并不能推断出它。
因此,欢迎您提供任何反馈意见。我错了吗,或者打字稿有限制吗?这是一个已知问题吗?谢谢
答案 0 :(得分:0)
这是发生这种情况的技术原因,也是一种解决方法:
Typescript的内置lib/es5.d.ts声明文件定义了Function.apply
,其参数类型为any
。还将Function.prototype
定义为“任意”。
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)