David Joos
David Joos

Reputation: 946

Property in type is not assignable to the same property in base type - Typescript bug?

Typescript complains because somehow the type of the abstract property is lost when extending. Is this a bug or am I doing something wrong?

type A = {
    bar: number,
    krass: Array<string>,
}

abstract class Foo<T> {
    protected abstract arr: (keyof T & string)[];
}

class Bar extends Foo<A> {
    arr = ["bar", "krass"];
}

Error msg:

Property 'arr' in type 'Bar' is not assignable to the same property in base type 'Foo<A>'.
  Type 'string[]' is not assignable to type '("bar" | "krass")[]'.
    Type 'string' is not assignable to type '"bar" | "krass"'.

Upvotes: 2

Views: 7958

Answers (2)

kaya3
kaya3

Reputation: 51102

Three possible solutions: if you don't mind (or actually prefer) the array being read-only, you can declare it as readonly and as const:

abstract class Foo<T> {
    protected abstract arr: readonly (keyof T & string)[];
}

class Bar extends Foo<A> {
    arr = ["bar", "krass"] as const;
}

Otherwise you need a type annotation in the subclass:

abstract class Foo<T> {
    protected abstract arr: (keyof T & string)[];
}

class Bar extends Foo<A> {
    arr: (keyof A & string)[] = ["bar", "krass"];
}

Or you could set this value in the base class's constructor, so that it's contextually typed by the super call:

abstract class Foo<T> {
    constructor(
        protected arr: (keyof T & string)[]
    ) {}
}

class Bar extends Foo<A> {
    constructor() {
        super(["bar", "krass"]);
    }
}

Upvotes: 1

Titian Cernicova-Dragomir
Titian Cernicova-Dragomir

Reputation: 250056

This is not a bug. It is by design. Members of derived classes are not contextually typed by the base class, so typescript will infer types based on whatever you are initializing with. This was discussed but I don't think it will get implemented anytime soon.

You can specify the type again in the derived class, or you could use an index access type to get the type from the base class:

class Bar extends Foo<A> {
    arr: Foo<A>['arr'] = ["bar", "krass"];
}

Playground Link

Upvotes: 1

Related Questions