Closed
Description
TypeScript Version:
2.3.4 and 2.4.1
Code
interface IBaseModel {
id: string
}
class BaseClass {
model: IBaseModel
constructor() {
}
setModel(model: IBaseModel) {
this.model = model
}
getValueByName(name: string) {
return this.model[name];
}
}
interface IDerived1Model extends IBaseModel {
height: number;
}
class Derived1 extends BaseClass {
// Unsound override!
setModel(model: IDerived1Model) {
super.setModel(model);
// Do something with model...
}
}
interface IDerived2Model extends IBaseModel {
width: number;
}
class Derived2 extends BaseClass {
// Unsound override!
setModel(model: IDerived2Model) {
super.setModel(model);
// Do something with model...
}
}
const model1 = { id: "0", height: 42 };
const model2 = { id: "1", width: 24 };
const obj1 = new Derived1();
obj1.setModel(model1);
const obj2 = new Derived2();
obj2.setModel(model2);
const objs: BaseClass[] = [
obj1,
obj2
];
let variable: any = null;
for (const obj of objs) {
if (obj instanceof Derived1) {
variable = obj.getValueByName("height"); // Ok, obj is now of type `Derived1`
} else if (obj instanceof Derived2) {
variable = obj.getValueByName("width"); // Does not compile: Property 'getValueByName' does not exist on type 'never'
}
console.log("Value is: " + variable);
}
Expected behavior:
obj
is narrowed to Derived2
.
Actual behavior:
obj
is narrowed to never
.
More
See discussion here: https://stackoverflow.com/questions/45381122/typescript-type-narrowed-to-never-with-instanceof-in-an-if-else-statement
When the setModel
overrides use IBaseModel
as type, it works. However, even though the current code is unsound, I would expect it to compile.