I have a problem when I try to use instanceof
with derived class instances in a if-else statement. Consider the following example:
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 {
setModel(model: IDerived1Model) {
super.setModel(model);
// Do something with model...
}
}
interface IDerived2Model extends IBaseModel {
width: number;
}
class Derived2 extends BaseClass {
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);
}
Here, getValueByName
cannot be called on obj
in the else
part, as it was narrowed to never
. Somehow, Typescript thinks that the else
will never be executed, but it is wrong.
The important thing to look at is the overriding of the function setModel
. The overrides have different parameter types, but those types inherit from the base IBaseModel
type. If I change those to the base type, Typescript doesn't complain and compiles fine :
class Derived1 extends BaseClass {
setModel(model: IBaseModel) {
super.setModel(model);
// Do something with model...
}
}
class Derived2 extends BaseClass {
setModel(model: IBaseModel) {
super.setModel(model);
// Do something with model...
}
}
So my question is, why does having overrides with different types make the instanceof
operator narrow the type of the object to never
? Is this by design?
This was tested with Typescript 2.3.4, 2.4.1 and in the Typescript Playground.
Thanks!
IBaseModel
). Also, what happens if I already have a member calledtype
on my object? Can it conflict withtype?
?. Thanks! – Vaginectomy