I have an array of objects that all implements the same generic Executable
interface:
interface Context {}
interface Executable<TContext extends Context> {
execute(context: TContext): void | Promise<void>;
}
I managed to infer a type that is the intersection of all the contextes passed to the execute function of each Executable in an array:
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
k: infer I
) => void
? I
: never;
type ContextUnion<TExecutables extends Executable<Context>[]> = TExecutables[number] extends Executable<infer TContext> ? TContext : never;
type IntersectContextesOf<TExecutables extends Executable<Context>[]> = UnionToIntersection<
ContextUnion<TExecutables>
>;
for instance:
interface RedContext { getRed(): string }
const red = { async execute(context: RedContext) {} }
interface BlueContext { getBlue(): string }
const blue = { async execute(context: BlueContext) {} }
const steps = [blue, red];
const context: IntersectContextesOf<typeof steps> = {
getRed() { return 'red' },
// Commenting the following line raise a TS error as expected
// getBlue() { return 'blue' },
}
The const context
is properly typed as intersection of blue
and red
execute's contextes.
But this seems to work only because the const steps
has no typing.
I tried to type steps
like this
const steps: Executable<Context>[] = [blue, red];
const context: IntersectContextesOf<typeof steps> = {
getRed() { return 'red' },
// No more TS Error
// getBlue() { return 'blue' },
}
Then context
has no more error with getBlue
commented, probably because the inference is turned off by forcing Context
in the steps
type.
I can't figure out a proper typing for the array of Executable.
The end game is to have an Operation
interface with steps
and a buildContext
method that return a context intersecting all the contextes of the steps:
interface Operation {
buildContext(): IntersectContextesOf<typeof this['steps']>;
steps: Executable<Context>[];
}
const operation: Operation = {
buildContext() {
return {
getRed() { return 'red' },
// getBlue() { return 'blue' },
}
},
steps: [blue, red],
}
But here again, commenting getBlue
does not raise a TS error