gpt4 book ai didi

在 if-else 语句中使用 instanceof 将 typescript 类型缩小到从不

转载 作者:行者123 更新时间:2023-12-03 19:37:57 24 4
gpt4 key购买 nike

我在尝试使用时遇到问题 instanceof在 if-else 语句中使用派生类实例。考虑以下示例:

interface IBaseModel {
id: string
}

class BaseClass {
model: IBaseModel
constructor() {
}

setModel(model: IBaseModel) {
this.model = model
}

getValueByName(name: string) {
return this.model[name];
}
}

interface IDerived1Model extends IBaseModel {
height: number;
}

class Derived1 extends BaseClass {
setModel(model: IDerived1Model) {
super.setModel(model);
// Do something with model...
}
}

interface IDerived2Model extends IBaseModel {
width: number;
}

class Derived2 extends BaseClass {
setModel(model: IDerived2Model) {
super.setModel(model);
// Do something with model...
}
}

const model1 = { id: "0", height: 42 };
const model2 = { id: "1", width: 24 };

const obj1 = new Derived1();
obj1.setModel(model1);

const obj2 = new Derived2();
obj2.setModel(model2);

const objs: BaseClass[] = [
obj1,
obj2
];

let variable: any = null;
for (const obj of objs) {
if (obj instanceof Derived1) {
variable = obj.getValueByName("height"); // Ok, obj is now of type `Derived1`
} else if (obj instanceof Derived2) {
variable = obj.getValueByName("width"); // Does not compile: Property 'getValueByName' does not exist on type 'never'
}
console.log("Value is: " + variable);
}

在这里, getValueByName无法调用 objelse部分,因为它缩小到 never .不知何故,Typescript 认为 else永远不会被执行,但这是错误的。

需要注意的重要一点是函数 setModel 的覆盖。 .覆盖有不同的参数类型,但这些类型继承自基 IBaseModel类型。如果我将它们更改为基本类型,Typescript 不会提示并且可以正常编译:
class Derived1 extends BaseClass {
setModel(model: IBaseModel) {
super.setModel(model);
// Do something with model...
}
}

class Derived2 extends BaseClass {
setModel(model: IBaseModel) {
super.setModel(model);
// Do something with model...
}
}

所以我的问题是,为什么具有不同类型的覆盖会使 instanceof运算符将对象的类型缩小到 never ?这是故意的吗?

这已使用 Typescript 2.3.4、2.4.1 和 Typescript Playground 进行了测试。

谢谢!

最佳答案

欢迎来到 TypeScript Issue #7271 的世界!你被 TypeScript 的 structural typing 咬了以及它与 instanceof 的奇怪(坦率地说是不健全的)互动.

typescript 见 Derived1Derived2作为完全相同的类型,因为它们具有相同的结构形状。如 obj instanceof Derived1返回 false ,TypeScript 编译器认为“好吧,obj 不是 Derived1”和“好吧,obj 不是 Derived2”,因为它没有看到它们之间的区别。然后当您检查 obj instanceof Derived2 时返回 true ,编译器说“哎呀,obj 既是又不是 Derived2 。这可能发生 never。”当然Derived1是有区别的和 Derived2在运行时,这可能会发生。这是你的问题。

解决方案:将一些不同的属性放入 Derived1Derived2这样 TypeScript 就可以分辨出它们之间的区别。例如:

class Derived1 extends BaseClass {
type?: 'Derived1'; // add this line
setModel(model: IDerived1Model) {
super.setModel(model);
// Do something with model...
}
}

class Derived2 extends BaseClass {
type?: 'Derived2'; // add this line
setModel(model: IDerived2Model) {
super.setModel(model);
// Do something with model...
}
}

现在有一个可选的 type每个类的属性具有不同的字符串文字类型(不更改发出的 JavaScript)。 TypeScript 现在意识到 Derived1Derived2 不一样你的错误就会消失。

希望有帮助。祝你好运!

更新 1

@sebastien-grenier said :

Thanks for the explanation! However, I fail to see why Typescript considers them structurally identical when the types of the parameter in the override is different, but everything compiles fine when the type is identical (i.e. the same as the parent, IBaseModel). Also, what happens if I already have a member called type on my object? Can it conflict with type? ?. Thanks!



哇,这很奇怪。好像有一个 change (#10216)在某些时候修复了问题 #7271 的某些实例,但您设法找到了一个新实例。我的猜测是因为您覆盖了 setModel具有更窄参数类型的方法(顺便说一句,这是不合理的......每个 BaseClass 都应该有一个 setModel() 接受任何 IBaseModel 。如果你有兴趣做到这一点,我们可以谈谈),它是愚蠢的#10216 中的代码更改为不应用。这可能是一个错误...你可能想要 file it .

是的,如果您已经拥有一个使用相同 key 的房产,您应该选择一个新的。这个想法是给类型打上烙印。您可以选择一个名称,例如 __typeBrand如果您担心意外冲突。

但是你可以做一个更直接的改变,它不会发生冲突:
class Derived1 extends BaseClass {
model: IDerived1Model;
// your overrides follow
}

class Derived2 extends BaseClass {
model: IDerived2Model;
// your overides follow
}

大概您希望每个类都知道它的 model是窄型的吧?所以做上述缩小 model两者都让编译器知道类型在结构上是不同的,并使派生类使用起来更安全。

干杯!

关于在 if-else 语句中使用 instanceof 将 typescript 类型缩小到从不,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45381122/

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