派生类是否在 Typescript 中实现了基类的接口

派生类是否实现基类的接口?

示例:

interface IBase {
    controls : { [key: string] : number }
}

class Baseclass implements IBase {
    controls = {};
}

class TestClass extends Baseclass {
    controls = {test:'a'};
}

当接口声明它应该是一个数字时,在派生类中将控件值设置为字符串时,上面不会抱怨。

如果我在派生类中将控件设置为 null,它会抱怨:

类型“null”不可分配给类型“{}”

在每个派生类和类中实现接口的方式是否正确?

xky09 回答:派生类是否在 Typescript 中实现了基类的接口

当一个类 implements 是一个接口时,它实际上并不影响类的类型。编译器检查类是否与接口兼容,但它不使用接口作为上下文来为类的成员提供类型。如果您编写 class Foo implements Bar {...} 并且没有编译器错误,那么编译器会像您省略 implements Bar 而只编写 class Foo {...} 一样对待它。有关详细信息,请参阅 microsoft/TypeScript#32082 和其中链接的问题。

因此推断 BaseClasscontrols 属性具有空对象类型 {}。您可能希望 BaseClasscontrols 属性属于 { [key: string] : number } 类型,但这不会发生,因为此类信息仅来自 implements IBase 子句,即为 BaseClass 的成员提供类型时忽略。如果你想看到一个实现类或子类具有特定类型的属性,你应该对它们进行注释:

class BaseClass implements IBase {
    controls: { [key: string]: number } = {}; // annotated
}

class TestClass extends BaseClass {
    // you might also want to annotate this,depending on intent
    controls = { test: 'a' }; // error!
}

同样重要的是要注意,无论好坏,TypeScript 的类型系统并不完全是 sound;有一些“漏洞”,TypeScript 允许分配不一致

在健全的类型系统中,subtyping 应该是 transitive;这意味着,对于任何类型 ABC,如果 A extends BB extends C,则 A extends C。虽然这在 TypeScript 中很常见,但有时会被违反。这就是您的示例中发生的情况:TestClass extends BaseClassBaseClass extends IBase,但 TestClass 不扩展 IBase

如果我们创建一个仅在 VerifyExtends<T,U> 时才编译的辅助类型函数 T extends U,我们可以亲眼目睹子类型化的这种不传递性:

type VerifyExtends<T extends U,U> = void;    
type AB = VerifyExtends<TestClass,BaseClass> // okay
type BC = VerifyExtends<BaseClass,IBase> // okay
type AC = VerifyExtends<TestClass,IBase> // error!

TestClass extends BaseClass 因为 controls 中的 TestClass 属性有一个额外的属性在 BaseClass ({}) 中没有提到,你被允许通过添加属性来扩展对象类型。

并且 BaseClass extends TestClass 因为它的 controls 属性 {} 具有与索引签名冲突的属性(它根本没有属性)......并且 TypeScript 将给出 implicit index signatures到这种类型。

但当然,TestClass 不会扩展 IBase,因为额外的属性和隐式索引签名不是相互一致的。所以我们有怪癖。


在您的情况下,我可能会建议使用明确的注释,因为这是您的意图。但你迟早会遇到不健全的情况,你应该为此做好准备。

Playground link to code

本文链接:https://www.f2er.com/441.html

大家都在问