Alternative for visitor pattern in TypeScript (avoiding instanceof conditionals)
You can use the approach the typescript compiler team used and have a field that discriminates against the type of field, replacing instanceof
with a simple string/number comparison, which is probably less expensive (although you should test for your use case):
enum Types {
A, B
}
interface I <T extends InternalResult> {
readonly type : Types;
doStuff(): T;
}
class A implements I <AResult> {
readonly type = Types.A
doStuff(): AResult {
return new AResult();
}
}
class B implements I <BResult> {
readonly type = Types.B
doStuff(): BResult {
return new BResult();
}
}
class Visitor {
private aExecuter: AExecuter = new AExecuter();
private bExecuter: BExecuter = new BExecuter();
visit(it: A | B): AResult | BResult {
// Since we don't have a default return, we will get a compiler error if we forget a case
switch(it.type){
case Types.A : return this.aExecuter.execute(it); break;
case Types.B : return this.bExecuter.execute(it); break;
}
}
}
Another approach which requires less writing (but is less type safe) is to use the constructor name as a key for access the correct method:
class Visitor {
private aExecuter: AExecuter;
private bExecuter: BExecuter;
visit(it: A | B): AResult | BResult {
let visitor: (it: A | B) => AResult | BResult = (this as any)['visit' + it.constructor.name];
if(visitor == null) {
throw "Visitor not found"
}
return visitor.call(this, it)
}
visitA(it: A): AResult {
return this.aExecuter.execute(it);
}
visitB(it: B): BResult {
return this.bExecuter.execute(it);
}
}