Let's assume I have an interface
interface I <T extends InternalResult> {
    doStuff(): T;
}
and two concrete classes implementing this interface:
class A implements I <InternalAResult> {
    doStuff(): InternalAResult {
        // implementation
    }
}
class B implements I <InternalBResult> {
    doStuff(): InternalBResult {
        // implementation
    }
}
My client cannot execute doStuff() directly, but needs an instance of a class implementing an Executer interface, like one of those:
interface Executer <T, R> {
     execute(it: T): R;
}
class AExecuter implements Executer <A, AResult> {
    execute(it: A): AResult {
        let internalResult = it.doStuff();
        // ... do something specific with internalResult create result
        return result;
    }
}
class BExecuter implements Executer <B, BResult> {
    execute(it: B): BResult {
        let internalResult = it.doStuff();
        // ... do something other specific with internalResult create result
        return result;
    }
}
For such cases I always used the Visitor pattern in Java. I could create a visitor, pass two Executer instances to it and implement the visit method with an A and a B overloading, creating a conditional-free solution, like this:
class Visitor {
    private aExecuter: AExecuter;
    private bExecuter: BExecuter;
    visit(it: A): Result {
        return this.aExecuter.execute(it);
    }
    visit(it: B): Result {
        return this.bExecuter.execute(it);
    }
}
Now, there is no such thing as method overloading in TypeScript / JavaScript. But what is the alternative to creating a conditional like this?:
if (it instanceof A)
    aExecuter.execute(it);
else if (it instanceof B)
    bExecuter.execute(it);
Note: I want to have the A class know nothing about AExecuter. This would increase the coupling between those two classes and I could not switch the implementation of AExecutor easily.
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);
    }
}
                        Maybe link the executor directly to the class, like:
A.prototype.Executor = ExecutorA;
B.prototype.Executor = ExecutorB;
So then you can just do
(new it.Executor).execute(it)
Alternatively one could use a Map to link the class and the Executor:
const executors: Map<I, Executor> = new Map();
executors.set(A, ExecutorA).set(B, ExecutorB);
which can be used as:
(new executors.get( it.constructor )).execute(it)
                        You can always skip the visitor pattern completely and use a multimethod library instead, then you can simplify your code to something like this:
import { multi, method, Multi } from '@arrows/multimethod'
interface IExecute extends Multi {
  (it: A): AResult
  (it: B): BResult
}
const execute: IExecute = multi(
  method(A, (it: A) => it.doStuff()),
  method(B, (it: B) => it.doStuff()),
)
// Usage:
execute(new A())
execute(new B())
It less type-safe (compiler won't warn you if you put a multimethod together incorrectly - but it's trivial code), but it is a much simpler solution.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With