I'm working on an application where we have defined a type and need to deduce multiple interfaces out from that single interface.
Example:
Our main type looks something like this. It is not important if this type is defined as a mapped type or union type, so whatever solution is easiest would be the best.
type A = {
type: "A"
input: {
type: "inputA"
}
output: {
type: "outputB"
}
}
type B = {
type: "B"
input: {
type: "inputB"
}
output: {
type: "outputB"
}
}
type Definitions = A | B
From those definitions, we want to construct a type which encapsulates the function interfaces.
type Program<F, K> = F extends { type: K; input: infer I; output: infer O }
? (input: I) => O
: never
type Programs = { [K in Definitions["type"]]: Program<Definitions, K> }
// Correctly deduced type:
// type Programs = {
// A: (input: {
// type: "inputA";
// }) => {
// type: "outputA";
// };
// B: (input: {
// type: "inputB";
// }) => {
// type: "outputB";
// };
// }
We can then provide these functions, something like this:
const a = (input: { type: "inputA" }): { type: "outputA" } => {
return { type: "outputA" }
}
const b = (input: { type: "inputB" }): { type: "outputB" } => {
return { type: "outputB" }
}
const programs: Programs = {
A: a,
B: b,
}
So good so far. However, I can't seem to wrap my head around how to dynamically call these functions. It is important that the run1
and run2
functions are typesafe.
const run1 = (definition: Definitions) => programs[definition["type"]](definition.input)
// throws error
// Argument of type '{ type: "inputA"; } | { type: "inputB"; }' is not assignable to parameter of type 'never'.
// The intersection '{ type: "inputA"; } & { type: "inputB"; }' was reduced to 'never' because property 'type' has conflicting types in some constituents.
// Type '{ type: "inputA"; }' is not assignable to type 'never'.
// or
type Input<F> = F extends { input: infer I } ? I : never
type Output<F> = F extends { output: infer O } ? O : never
type Type<F> = F extends { type: infer T } ? T : never
const run2 = <F>(type: Type<F>, input: Input<F>): Output<F> => {
return programs[type](input)
}
// yields error
// Type 'Type<F>' cannot be used to index type 'Programs'.
Anything I'm missing?