Description
TypeScript Version: 2.7.0-dev.201xxxxx
Search Terms: inference, generics, static, implemenation
Code
I had encountered a problem with TypeScript which I had a question about on StackOverflow.
This was answered by @jcalz in which he stated he couldn't find any match of the exact same problem, that is why I create this issue.
StackOverflow question: https://stackoverflow.com/questions/49056870/typescript-infers-a-more-general-type-at-static-implementation
The problem is with the code as follows:
enum FruitList {
APPLE,
BANANA,
PEAR,
}
interface FruitInterface {
fruit: FruitList;
}
abstract class Fruit implements FruitInterface {
fruit: FruitList;
constructor(fruit: FruitList) {
this.fruit = fruit;
}
}
interface AppleInterface extends Fruit {
fruit: FruitList.APPLE;
}
class Apple extends Fruit implements AppleInterface {
fruit: FruitList.APPLE;
constructor(fruit: FruitList) {
super(fruit);
}
}
Here AppleInterface
declares the field fruit
to be of type FruitList.APPLE
while the parent interface FruitInterface
declares field 'fruit' to be of type 'FruitList. So far so good. Now
Fruithas a constructor with the parameter
fruitof type
FruitListwhich becomes the value of the field
fruitand its child class
Appledoes the same however its field is not of type
FruitListbut of type
FruitList.APPLE`. Even though this will is likely to cause issues, it is accepted by TypeScript. The goal of TypeScript is not to be completely sound however the solution to this will quickly become cumbersome.
The solution is as follows:
interface FruitInterface<T extends FruitList> {
fruit: T;
}
abstract class Fruit<T extends FruitList = FruitList> implements FruitInterface<T> {
fruit: T;
constructor(fruit: T) {
this.fruit = fruit;
}
}
interface AppleInterface extends Fruit<FruitList.APPLE> {
}
class Apple extends Fruit<FruitList.APPLE> implements AppleInterface {
constructor(fruit: FruitList) {
super(fruit); // now error as you expect
}
}
This seems like a great solution however when there are many fields which's type can be narrowed by its child classes, all those fields must be declared by a generic type extending the base type. This can be really cumbersome. Could this be fixed?
Expected behavior:
TypeScript complains about the child parameter type being of a wider type than the field it gets assign to in its parent constructor because the child narrowed the field its type.
Actual behavior:
TypeScript allows the child parameter type being of a wider type than the field it gets assigned to in the parent constructor when its being narrowed by the child.
Related Issues: #1394