我目前正在调试一些库的代码,并且我一生都不理解特定的错误。
这是我正在调试的代码的简化示例:
type Test1Type<V> = object | ((this: V) => object);
function testFnc(x: string, test1Fnc: Test1Type<string>) {
let res;
if (typeof test1Fnc === "function") {
res = test1Fnc.call(x, x);
}
}
Run Code Online (Sandbox Code Playgroud)
在该行中,res = test1Fnc.call(x, x);该术语test1Fnc被标记为错误:
(parameter) test1Fnc: Function | ((this: string) => object)
The 'this' context of type 'Function | ((this: string) => object)' is not assignable to method's 'this' of type '((this: string) => object) & Function'.
Type 'Function' is not assignable to type '((this: string) => object) & Function'.
Type 'Function' is not assignable to type '(this: string) => object'.
Type 'Function' provides no match for the signature '(this: string): object'.ts(2684)
Run Code Online (Sandbox Code Playgroud)
的内部类型定义.call()似乎是这样的:
call<T, A extends any[], R>(this: (this: T, ...args: A) => R, thisArg: T, ...args: A): R;
不幸的是,我只是无法弄清楚这段代码出了什么问题,特别是因为我正在调试的库直接在其代码库中包含了这段代码(请参阅 参考资料)。
我真的不明白错误消息在这里告诉我什么。
我正在使用"typescript": "~3.9.3"(图书馆正在使用"typescript": "^2.8.3")。
感谢任何帮助,因为我真的很想了解这里发生的事情。
Sim*_*obs 13
Function您发现 TypeScript 处理联合类型交互的方式不一致。
当call调用时,编译器知道test1Fnc是:
Function对象,或者Function一个在字符串上调用的JavaScript对象(即它有一个字符串作为this参数)并返回一个对象。然后,编译器会询问该调用对于每种类型是否有效。
当它询问该调用对于类型 1 是否有效时,它会得出结论该调用无效,因为test1Fnc可能无法在字符串上调用。
这很奇怪,因为这与对象的常规 TypeScript 行为相反Function,它通常允许任何类型的函数调用。要查看是否允许此类调用,请暂时将初始类型声明更改为type Test1Type<V> = object,错误就会消失。
发生这种情况是因为有关Function编译器如何处理类型的两件事是正确的:
当编译器知道test1Fncis a Functiononly 时(如类型声明更改时),它会根据规则 1 起作用。但是当涉及联合类型时,它首先尝试将联合类型分配给该call类型的给定签名。由于规则 2 在规则 1 有机会覆盖它之前,该分配就失败了。
您可以看到这不是预期的行为,因为 ifTest1Type<V>被定义为这两种类型中的一种(假设进行了下一节中解释的修改),那么编译器不会给出任何错误。这是不对的:如果程序的路径对于联合类型中的任一类型都有效,则该程序应该对联合类型有效。
作为另一个观察,即使test1Fnc是上面的类型 2,它也不会无法用call所列出的函数来调用。给定call函数需要一个this值string以及一个类型 的常规参数string,但上面的类型 2 只是一个没有常规参数this的类型值。string编译器目前没有显示此错误,但如果将初始类型声明更改为type Test1Type<V> = ((this: V) => object)then 您将看到产生不同的错误。反过来,通过将行更改为 可以消除此问题type Test1Type<V> = ((this: V, arg: V) => object)。
为了解决这个问题,您可以考虑以下一项或多项:
Test1Type<V> = object | ((this: V, arg: V) => object),这似乎是代码所期望的。这并没有真正改变类型,因为它仍然可以是任何对象,但它确实更好地表达了代码的意图test1Fnc然后,如果您可以将块内的类型强制转换if为预期类型,则可以清除错误:res = (test1Fnc as (this: string, arg: string) => object).call(x,x)。如果您无法进行这样的更改,您可能不得不放弃任何类型安全并转向type Test1Type = objectFunction可能不鼓励使用对象进行打字,并且Test1Type<V>可能需要哪些其他值,但如果您可以进行更广泛的更改,最好将其编写为更显式类型的联合,以便编译器可以在它们之间进行选择,或更严格地以完全 OOP 风格建立类层次结构。| 归档时间: |
|
| 查看次数: |
8269 次 |
| 最近记录: |