Reputation: 946
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
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
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"];
}
Upvotes: 1