简体   繁体   中英

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

I am writing an object interface, one of which is an attribute value is a class . But ts compiler will throw an exception when this class extends other classes .

Here is a simple example:

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
}

The error content:

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?

And when I delete the extends in Foo , the error disappeared:

class Foo { } // < delete extends here

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

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

In my understanding, Foo extends from Base , so it should include the relevant attributes in Base , but the compiler tells me it does not.

So is there any way to solve this problem?

Error with Generics

There is another question. When I use generics on Obj because there are multiple type extends Base . like this:

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 
}

compiler will throw an exception as below:

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

Is there a way to make the generic definition a bit more complete?

version I used

  • node 12.16.1
  • typescript 3.8.3 and 4.3.4

value in your Obj interface is an instance of the Foo class, not the class itself. So assigning an object whose value property is the Foo class to a variable of the interface type is an error. You should set value to an instance of Foo (such as new Foo() , which is what the compiler error message suggests), or if you really meant to use Foo , declare value: typeof Foo in Obj .

The reason this works when Foo does not extend Base is TypeScript's structural typing: Foo does not have any properties, so any object, including a class, can be assigned to a variable of that type, even though it may not be an actual instance of Foo at runtime.

In response to the additional question regarding generics:

typeof T is not valid when T is a type parameter, because T does not have to be a class. It can be an interface that is structurally compatible with Base , and interfaces do not have an "interface object" whose type can be retrieved with typeof . You could use this definition of Obj , which works but requires using typeof at use sites:

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

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

key is a required field in your class Base . That's why that error. You can either make the property key optional or write constructors in Base and Foo .

Making key property optional:

class Base {
    key?: string
}

class Foo extends Base { }

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

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

Making key mandatory property:

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()
}

Additionally, the obj initialization should do new Foo() for property value or the type of value inside Obj interface should be typeof Foo .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM