When attempting to write a legacy function within our codebase that invokes methods on certain objects while also handling errors, I encountered difficulty involving the accuracy of the return type. The existing solution outlined below is effective at constraining the action
type but falls short in providing a precise return type (defaults to any
).
The main goal of the entityGetter
function is to accept either entityA or entityB and only callable keys that start with 'get'
. This specific aspect works as expected, indicating that achieving a correct return value should be feasible; yet the issue lies in why the action
does not have the appropriate type when accessing entity
.
I've experimented with setting defaults for T and A, which unfortunately did not yield any changes.
Is it viable to address this without modifying the (runtime) interface of the entityGetter
function? Ideally, I'd like to avoid making JavaScript alterations altogether, although implementing changes within the function's body would be acceptable.
type TEntityA = {
getA: () => string;
getB: () => string;
setC: (arg0: string) => string;
valD: number;
getE: () => object;
};
type TEntityB = {
getA: () => string;
getB: () => number;
setC: (arg0: string) => string;
valD: string;
};
type TEntities = TEntityA | TEntityB;
// type KeysOfType = https://stackoverflow.com/a/64254114
type TGetterKeys<T extends TEntities> = keyof Pick<T, keyof T & (`get${string}`)>;
type TCallableKeys<T extends TEntities> = KeysOfType<T, () => unknown>;
type TGetters<T extends TEntities> = TGetterKeys<T> & TCallableKeys<T>;
const entityGetter = <
T extends TEntities,
A extends TGetters<T>
>(action: A, entity: T) => entity[action]();
// This expression is not callable. Type 'unknown' has no call signatures.
const entityA = new EntityA();
const entityB = new EntityB();
const AgetA = entityGetter('getA', entityA); // => string
const BgetB = entityGetter('getB', entityB); // => number
const AgetX = entityGetter('getX', entityA);
// Argument of type '"getX"' is not assignable to parameter of type '"getA" | "getB" | "getE"'.