I’ve been playing around with TypeScript (I’m still learning) and writing up a few test scenarios to better understand programming fundamentals.
export class Base {
readonly id: string;
constructor(id: string) {
this.id = id;
}
}
export class A extends Base {
image: string;
constructor(id: string, image: string) {
super(id);
this.image = image;
}
}
export class B extends Base {
occupancy: number;
constructor(id: string, occupancy: number) {
super(id);
this.occupancy = occupancy;
}
}
export class C extends Base {
noiseLevel: string;
constructor(id: string, noiseLevel: string) {
super(id);
this.noiseLevel = noiseLevel;
}
}
function doSomething(param: A | B | C): string {
if (param instanceof Base) {
return 'base';
} else if (param instanceof A) {
return 'a';
} else if (param instanceof B) {
return 'b';
} else if (param instanceof C) {
return 'c';
}
}
Say I tried the following:
doSomething(new A('idA', 'image');
doSomething(new B('idB', 1);
doSomething(new C('idC', 'high');
I would expect them to all return with the string 'base'
, because they are all subclasses of Base
, and this was the first condition in the sequence to be checked.
I was OK with that, as I’m aware of polymorphism… but I wasn’t able to even write the function. And the reason why is what’s leaving me a bit confused.
When I hover over the word param
for any of the else if
‘s I get the error:
The left-hand side of an 'instanceof' expression must be of type 'any', an object type, or a type parameter. ts(2358)
(parameter) param: never
I can’t seem to find much about this online, at least nothing that really answers this for me.
Is it insinuating that Base
is not a valid type? There isn’t anything particularly wrong with Base
itself, it’s not something strange like an abstract class or anything like that (at least not at face value)… of course, it’s being used as a superclass in our scenario, but the wording of the error makes it sound like the problem is with Base
as a singularity. If the problem was with Base
by itself, surely this error would have been flagged for the initial if
too? But it isn’t.
Hopefully, someone can help shine a bit of light on this. I’m finding it challenging as a beginner!
Thank you.
EDIT:
I wasn’t very clear at the start, my apologies.
My compiler won’t even let me write the function:
function doSomething(param: A | B | C): string {
if (param instanceof Base) {
return 'base';
} else if (param instanceof A) {
return 'a';
} else if (param instanceof B) {
return 'b';
} else if (param instanceof C) {
return 'c';
}
}
Ergo, I was never even able to get as far as:
doSomething(new A('idA', 'image');
doSomething(new B('idB', 1);
doSomething(new C('idC', 'high');
5
Try param: Base | A | B | C
, maybe the compiler is complaining since you are checking instanceof Base
which it thinks applies only to the case where its checking for a pure Base
instance and not A, B, or C. Basically, the typescript type system cannot a account for the niche behavior in javascript that instanceof the base class will return true.
Either way, I recommend being more explicit about type guards
like
class Base {
name: "Base"
constructor(){
this.name="Base"
}
static is(value: unknown): value is Base{
return value instanceof Base
}
static isLike(value: unknown): value is Base | BaseData {
return typeof value === "object" && value !== null && (value as BaseData).name==="Base"
}
static from(obj: Base | BaseData){
if(Base.is(obj)){
return obj
}
else {
// specific to your class and its constructor
return new Base(...specific code...)
}
}
toObject(): BaseData {
return Object.assign({},this)
}
}
type BaseData = {
[K in keyof Base]: Base[K]
}