gpt4 book ai didi

Typescript 类型谓词结果为 never

转载 作者:行者123 更新时间:2023-12-01 12:04:19 26 4
gpt4 key购买 nike

以下 typescript 片段在严格模式下重现了(编译器)问题,编译后的代码运行良好:

class ClassX
{
constructor(public label: string) {}
}

class ClassA extends ClassX
{
constructor() { super('A'); }
}

class ClassB extends ClassX
{
constructor() { super('B'); }
}

type TClass = ClassA | ClassB;

class Wrapper<T extends TClass>
{
constructor(public source: TClass)
{
if(Wrapper.IsB(this)) console.log(this.source.label);

// Works normally:
// if(source instanceof ClassA) this.Log();
// else if(source instanceof ClassB) this.Log();

if(Wrapper.IsA(this)) console.log(this.source.label);
// this results in 'never', would emit error TS2339 without the type guard
else if(Wrapper.IsB(this)) console.log((this as Wrapper<ClassB>).source.label);
}

public static IsA(wrapper: Wrapper<TClass>): wrapper is Wrapper<ClassA>
{
return wrapper.source instanceof ClassA;
}

public static IsB(wrapper: Wrapper<TClass>): wrapper is Wrapper<ClassB>
{
return wrapper.source instanceof ClassB;
}
}

console.log('ClassA');
new Wrapper(new ClassA()); // logs 'A'

console.log('\nClassB');
new Wrapper(new ClassB()); // logs 'BB'

我怀疑编译器正在缩小通用基类型 ClassX 的范围,但我并没有针对基类进行测试!就 instanceof 而言,子类是否优先于基类?

我错过了什么?

最佳答案

初步估计,TypeScript 的类型系统是 structural , 不是 nominal .这意味着输入 A然后输入 B当且仅当它们具有相同的结构,而不是它们具有相同的名称(或者更准确地说,声明)时,它们才被认为是 TypeScript 中的相同类型。这也意味着输入 A然后输入 B当且仅当它们具有不同结构时才被认为是不同类型,而不仅仅是它们具有不同名称(或声明)。

在您示例的代码中,ClassAClassB具有相同的结构,因此编译器认为它们是同一类型。如果 x is ClassA 上的类型保护返回 false , 然后编译器认为 x不是 ClassA , 因此它也不是 ClassB .这显然不是您的意图;你想要ClassAClassB被认为是不同的类型。解决此问题的一个简单方法是 add a private property to each class ,或任何两个不同的属性,像这样:

class ClassA extends ClassX {
readonly name = "A"; // type name is string literal "A"
constructor() { super('A'); }
}

class ClassB extends ClassX {
readonly name = "B"; // type name is string literal "B"
constructor() { super('B'); }
}

这给出了 ClassA一个name string literal 的属性(property)输入 "A" , 和 ClassB一个name字符串文字类型的 "B" .编译器现在将它们视为不同的,一切都很好,对吧?


错了!问题仍然存在,因为与 Wrapper<T> 存在相同问题.在这种情况下,您的 Wrapper<T>does not depend structurally on T .编译器认为 Wrapper<ClassA> 之间没有区别和 Wrapper<ClassB>即使ClassAClassB是不同的。你可以这么说,因为T出现在类名中,而在定义中无处可寻。由于类型系统不是名义上的,名称 Wrapper<ClassA> 并不重要不同于Wrapper<ClassB> .它们是同一类型。

我假设您可能希望构造函数参数为 public source: T而不是 public source: TClass ,像这样:

constructor(public source: T) { ... }

这会产生 Wrapper<T> 的效果有一个source类型属性 T ,因此 Wrapper<ClassA>Wrapper<ClassB>将是不同的,因为 source.name将是不同的类型。

现在 this不会减少到 never你防范之后Wrapper<ClassA>Wrapper<ClassB>分别:

constructor(public source: T) {
if (Wrapper.IsA(this)) console.log(this.source.label);
else if (Wrapper.IsB(this)) console.log((this).source.label); // okay
}

好的,希望对您有所帮助。祝你好运!

Link to code

关于Typescript 类型谓词结果为 never,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59338237/

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