I’m building a TypeScript class that needs to accept multiple parameters of the type EscapeHandlerSettings
.
type EscapeHandlerSettings<
E extends string,
N extends string,
I extends InitiliazerKeys,
P extends ParameterSchema,
T extends Terminator
> = {
eventname: E,
namespace?: N,
initializer?: I
parameters?: P,
terminator?: T,
callback: (
params: { [parameter in keyof P]: ParameterTypesMap[P[parameter]]["type"] },
setup: { eventname: E, namespace: N, initiliazer: I, terminator: T[number] }
) => any
}
However, each of these EscapeHandlerSettings
should have its own generics because the parameters passed to the callback
and setup
depend on the generic parameters of that specific configuration.
For example, when the first parameter passed to the constructor is:
{
eventname: "event1",
namespace: "namespace1",
initializer: "ESC",
parameters: { attr1: "string?", attr2: "number" },
terminator: ["A", "B", "C", "D"] as const,
callback: (params, setup) => { }
}
This means that the params
in the callback
function should have the following type:
{
attr1: string | undefined;
attr2: number;
}
And the setup
parameter should have the type:
{
eventname: "event1";
namespace: "namespace1";
initializer: "ESC";
terminator: "A" | "B" | "C" | "D";
}
I want to create a constructor for my EscapeHandler
class that can accept multiple configurations of this type, where each configuration should have its own specific generics. Right now, I’m trying to make the class accept an indefinite number of these configurations, but each one should have its own types inferred based on the configuration passed.
Here is the class definition and an example of how it should be used:
class EscapeHandler<
E1 extends string,
N1 extends string,
I1 extends InitiliazerKeys,
P1 extends ParameterSchema,
T1 extends Terminator,
E2 extends string,
N2 extends string,
I2 extends InitiliazerKeys,
P2 extends ParameterSchema,
T2 extends Terminator
E extends string[],
N extends string[],
I extends InitiliazerKeys[],
P extends ParameterSchema[],
T extends Terminator[]
> {
constructor(...settings: [
EscapeHandlerSettings<E1, N1, I1, P1, T1>,
EscapeHandlerSettings<E2, N2, I2, P2, T2>?,
...EscapeHandlerSettings<
E[number], N[number], I[number], P[number], T[number]
>[] // <-- How can I make this infinite and allow each element to have its own generic types?
]) { }
}
usage:
new EscapeHandler(
{
eventname: "event1",
namespace: "namespace1",
initializer: "ESC",
parameters: { attr1: "string?", attr2: "number" },
terminator: ["A", "B", "C", "D"] as const,
callback: (params, setup) => { }
},
{
eventname: "event2",
parameters: { attrA: "string?", attrB: "number?" },
terminator: ["X", "Y", "Z"] as const,
callback: (params, setup) => { }
},
{
eventname: "event3",
namespace: "namespace3",
initializer: "OSC",
parameters: { attrK: "string?", attrL: "number?" },
terminator: ["X", "Y", "Z"] as const,
callback: (params, setup) => { } <- is not inferring literally
}
)
How can I define the class constructor so that each element passed to the EscapeHandler
can have its own generic parameters, and the types for params
and setup
are properly inferred for each configuration?
An alternative I found was:
type EscapeHandlerSettings<
E extends string[],
N extends string[],
I extends (undefined | InitiliazerKeys)[],
P extends ParameterSchema[],
T extends Terminator[],
> = (
& { [K in keyof E]: { eventname: E[K] } }
& { [K in keyof N]: { namespace: N[K] } }
& { [K in keyof I]?: { initializer: I[K] } }
& { [K in keyof T]: { terminator: T[K] } }
& { [K in keyof P]: {
parameters: P[K],
callback: (
params: {
[parameter in keyof P[K]]: ParameterTypesMap[P[K][parameter]]["type"]
},
setup: any
) => any
} }
)
But I couldn’t figure out how to make the attributes optional. I tried to use the ?
operator, but it didn’t work. I also tried to use the Partial
type, but it didn’t work either. I’m not sure if it’s possible to make the attributes optional in this case.
1