gpt4 book ai didi

typescript - 通过扩展和实现的组合理解 TS 的类型推断/缩小

转载 作者:行者123 更新时间:2023-12-05 03:17:25 25 4
gpt4 key购买 nike

我有以下示例代码

class B implements Error {
name: string = '';
message: string = '';
// stack?: undefined | string;
}

function Foo(x: any) {
if(x instanceof Error) {
if(x instanceof B) {
x.stack; // works
}
}
}

abstract class C {
someProp: number = 0;
}

class D extends C implements Error{
name: string = '';
message: string = '';
// stack?: undefined | string
}

function Bar(x: any) {
if(x instanceof Error) {
if(x instanceof D) {
x.stack // does not work: Property 'stack' does not exist on type 'D'.
}
}
}

我真的不明白为什么尝试访问 Bar(..) 中的 stack 失败但在 Foo(..) 中有效.

为什么 extends 使 Bar 在外部 if 缩小到 Error 后失败?

最佳答案

这里发生了一些事情,但我想说主要问题是 TypeScript 的类型系统是 not completely sound ,特别是在optional properties附近.

TypeScript 允许您将没有已知属性的对象分配给具有该属性作为可选属性的类型。所以 {x: string} 可以分配给 {x: string, y?: number}。这非常方便,但并不合理,因为所有编译器真正知道类型 {x: string} 的值是它具有 x 类型的属性 字符串。它对y 属性一无所知,所以它不知道y 属性是一个数字 或丢失。这是一个known unsoundness在 TypeScript GitHub 存储库问题列表中的几个地方提到,例如 microsoft/TypeScript#47499 .同样,它很方便;想象一下,如果您编写了 const z = {x: "hello"} 然后无法z 分配给 类型的变量{x: string, y?: number}.那会很安全,但使用起来很烦人,人们会不高兴。

TypeScript 还允许(相对)合理 将具有已知属性(可选或其他)的对象分配给没有该已知属性的类型的操作。所以 {x: string, y?: number} 可以分配给 {x: string}。对象类型是开放的,而不是密封的。 (有关详细信息,请参阅 this Q/A。)

结合这些意味着具有可选属性的类型可以与没有已知属性的类型相互分配,因此当编译器将一种类型缩小或扩大到另一种类型时,它最终可能会忘记可选属性,具体取决于确切的操作集和顺序。


您的示例代码等同于

class B {
name: string = '';
message: string = '';
}

function Foo(x: any) {
if (x instanceof Error) {
if (x instanceof B) {
x.stack; // okay
}
}
}

class D {
someProp: number = 0;
name: string = '';
message: string = '';
}

function Bar(x: any) {
if (x instanceof Error) {
if (x instanceof D) {
x.stack // error!
}
}
}

请注意,类中的 implements 子句对其实例类型没有影响(有关更多信息,请参见 this Q/A),因此您最好删除 implements Error因为它什么都不做。类似地,您类的 extends C 部分仅用于让 D 继承 someProp 属性,因此您不妨直接在 中定义它>D.

这里的问题是 Error 被认为可分配给 B,即使它缺少 Error 的可选 堆栈 属性。当您将 xError 缩小到 B 时,编译器根本不会认为它需要缩小。类型保持为 Error,因此 x 具有可选的 stack 属性。

另一方面,Error 不能分配给 D,因为 D 有一个必需的 someProp 属性Error 不一定有。 D 被认为比 Error 更窄。当您将 xError 缩小到 D 时,编译器会尽职地缩小到 D现在 x 没有已知的 stack 属性,因为 D 没有。

就这样吧。 TypeScript 有一些为了方便而允许的不健全性,这种不健全性的影响可能会突然出现在一些奇怪的地方。

Playground link to code

关于typescript - 通过扩展和实现的组合理解 TS 的类型推断/缩小,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/74127866/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com