1

Is it possible to achieve the following in Typescript:

I want the type of state.initial to be deduced as a tuple based on the input to takeConfig. It would also be nice to deduce the return type of initialState based on the type of the state property of the component function's props argument (Although the latter, I believe, is not possible without changing 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!

1
  • If your question is "is it possible" and not "how to do it" then the answer is yes. Commented May 6, 2022 at 14:41

1 Answer 1

1

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.

Playground

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.