Is it possible to achieve the folowing in Typescript:
I want the type of state.initial
to be deduced as a tuple based on the input to the takeConfig
. It would also be nice to deduce the return type of initialState
functions based on the type of state
property of component's argument (Although the latter I believe is not possible without the alteration of this API).
type State1 = { a: string };
type State2 = { b: string };
function Component1(props: { state: State1 }) {}
function Component2(props: { state: State2 }) {}
const state = takeConfig([
{
component: Component1,
initialState: () => ({
a: "a"
}) // return type here is deduced to be State1
},
{
component: Component2,
initialState: () => ({
b: "b"
}) // return type here is deduced to be State2
},
]);
// type here is deduced to be [State1, State2]
state.initial
Thanks!
CodePudding user response:
If your question is "is it possible" and not "how to do it" then the answer is yes.
Let's first define the type of an object that your takeConfig
function would take:
type Conf<C extends (...args: any[]) => any> = {
component: C;
initialState: () => Parameters<C>[0]["state"];
};
Then we'll make a type to infer all the component function types in the tuple:
type InferConfs<C extends ReadonlyArray<unknown>> = {
[K in keyof C]: C[K] extends Conf<infer Comp> ? Conf<Comp> : C[K];
};
Next we'll need a type to give us state.initial
in a similar manner to InferConfs
:
type JustStates<C extends ReadonlyArray<unknown>> = {
[K in keyof C]: C[K] extends Conf<infer _> ? ReturnType<C[K]["initialState"]> : C[K];
};
Finally let's create a type to hold JustStates
:
type ReturnedState<Confs extends ReadonlyArray<unknown>> = {
initial: JustStates<Confs>;
};
And also a type to remove the readonly
that as const
will give:
type MakeNotReadonly<T> = {
-readonly [K in keyof T]: T[K];
};
Now we put all of these together to define our takeConfig
function:
function takeConfig<C extends ReadonlyArray<unknown>>(conf: InferConfs<C>): ReturnedState<MakeNotReadonly<InferConfs<C>>> { ... }
You must implement the function yourself, however. I am just helping with the types.
The basic principle is that TypeScript can infer the general type that we pass to takeConfig
, and we use InferConfs
to further narrow the types of the configs. Then in the return type we infer the configs yet again, then do some type transformations (JustState
) on it and return it.