I possess a collection of functions mapped with various signatures
const allMyFunctions = {
f1: () => Promise.resolve(1),
f2: (a: number) => a
};
Furthermore, I have an abstract class that performs operations on these functions:
abstract class MyClass {
protected abstract decorateMethod(...args: any[]): any;
decorateAll() {
return Object.keys(allMyFunctions)
.reduce((acc, key) => {
const f = allMyFunctions[key as keyof typeof allMyFunctions];
acc[key as keyof typeof allMyFunctions] = this.decorateMethod(f);
return acc;
},{} as {[index in keyof typeof allMyFunctions]: ReturnType<MyClass['decorateMethod']>});
}
}
Hence, by invoking decorateAll
, a fresh map is generated with identical keys, but each function undergoes decorateMethod
, which is abstract.
I am now able to instantiate two concrete classes like so:
class MyConstantClass extends MyClass {
protected decorateMethod(f: AnyFunction) {
return () => 2;
}
}
class MyIdentityClass extends MyClass {
protected decorateMethod<F extends AnyFunction>(f: F) {
return f;
}
}
const instance1 = new MyConstantClass();
const a = instance1.decorateAll().f1;
const instance2 = new MyIdentityClass();
const b = instance2.decorateAll().f2;
Unfortunately, the types of a
and b
are currently any
, when ideally they should be () => number
and (a:number) => number
.
It appears that by duplicating the logic from decorateAll
implementation in child classes and updating the final line cast to
ReturnType<MyIdentity['decorateMethod']>
, the issue can be resolved. However, this leads to redundant code repetition, which I aim to avoid through the utilization of the abstract class initially.
Edit: Include playground link