Typescript class extends from other class 不能用作接口属性

Typescript class extends from other class cannot be used as interface attributes

我正在写一个对象接口,其中一个是一个属性值是一个class。但是当这个 class extends other classes.

时,ts 编译器会 抛出异常

这是一个简单的例子:

class Base {
    key: string
}

class Foo extends Base { }

interface Obj {
    list: Foo[]
    value: Foo
}

const obj: Obj = {
    list: [new Foo()],
    value: Foo // < Throws error here
}

错误内容:

Property 'key' is missing in type 'typeof Foo' but required in type 'Base'.ts(2741)
test.ts(39, 5): 'key' is declared here.
test.ts(51, 12): Did you mean to use 'new' with this expression?

当我删除 Foo 中的扩展时,错误消失了:

class Foo { } // < delete extends here

interface Obj {
    list: Foo[]
    value: Foo
}

const obj: Obj = {
    list: [new Foo()],
    value: Foo // < No error throw
}

在我的理解中,Foo扩展自Base,所以它应该包含Base中的相关属性,但编译器告诉我它没有。

那么有什么办法可以解决这个问题吗?

泛型错误

还有一个问题。当我在 Obj 上使用泛型时,因为有多个类型扩展 Base。像这样:

class Foo extends Base { }

class Bar extends Base { }

interface Obj<T extends Base> {
    list: Foo[]
    value: typeof T // < Throws error here
}

const obj: Obj<Foo> = {
    list: [new Foo()],
    value: Foo 
}

编译器会抛出如下异常:

'T' only refers to a type, but is being used as a value here.

有没有办法使通用定义更完整一些?

我使用的版本

  • 节点12.16.1
  • typescript 3.8.34.3.4

key 是 class Base 中的必填字段。这就是那个错误的原因。您可以使 属性 key 可选或在 BaseFoo.

中编写构造函数

使 key 属性 可选:

class Base {
    key?: string
}

class Foo extends Base { }

interface Obj {
    list: Foo[]
    value: Foo
}

const obj: Obj = {
    list: [new Foo()],
    value: new Foo()
}

强制执行 key 属性:

class Base {
    key: string

    constructor(key: string) {
        this.key = key;
    }
}

class Foo extends Base {
    constructor(key: string) {
        super(key);
    }
}

interface Obj {
    list: Foo[]
    value: Foo
}

const obj: Obj = {
    list: [new Foo()],
    value: new Foo()
}

此外,obj 初始化应该为 属性 valuenew Foo() 或者 Obj 接口中 value 的类型应该是typeof Foo.

value 在您的 Obj 界面中是 实例 Foo class,而不是 class 本身。因此,将 value 属性 是 Foo class 的对象分配给接口类型的变量是错误的。您应该将 value 设置为 Foo 的实例(例如 new Foo(),这是编译器错误消息所建议的),或者如果您真的打算使用 Foo,请声明value: typeof FooObj.

Foo 不扩展 Base 时它起作用的原因是 TypeScript 的结构类型:Foo 没有任何属性,所以 any 对象,包括 class,可以分配给该类型的变量,即使它在运行时可能不是 Foo 的实际实例。

回答关于泛型的附加问题:

T 是类型参数时,

typeof T 无效,因为 T 不一定是 class。它可以是一个在结构上与 Base 兼容的接口,并且接口没有可以用 typeof 检索类型的“接口对象”。您可以使用 Obj 的这个定义,它有效但需要在使用站点使用 typeof

interface Obj<T extends new (...args: any[]) => Base> {
    list: Foo[],
    value: T
}

const obj: Obj<typeof Foo> = {
    list: [new Foo()],
    value: Foo
}