Type inference according to parameter

52 Views Asked by At

In the following case, is it possible to correctly infer the type of result correctly as boolean?

interface ActionWithPayload<T extends string, K> { type: T, payload: K }

function ofType<T extends ActionWithPayload<string, any>>(...param: T["type"][]): T extends ActionWithPayload<typeof param[number], infer U> ? U : never {
    return null;
}

enum one {
    foo = "foo",
    bar = "bar"
}

type action = ActionWithPayload<one.foo, boolean> | ActionWithPayload<one.bar, string>;

var result = ofType<action>(one.foo); // type of result should be boolean

Playground link

1

There are 1 best solutions below

0
Titian Cernicova-Dragomir On BEST ANSWER

The problem is that T["type"] when T is action will be one.foo | one.bar regardless of what parameters you pass. You need an extra generic parameter so the compiler will infer the literal type for the enum member you pass in:

function ofType<T extends ActionWithPayload<string, any>, K extends one = one>(...param:K[]): T extends ActionWithPayload<K, infer U> ? U : never {
    return null as any;
}
var result = ofType<action, one.foo>(one.foo); // will be boolean

The disadvantage is that you have to explicitly specify the literal type one.foo since you can't specify just one of the type parameters. As an alternative you could use a two function approach so you can specify the type parameter to the first function and let inference work for the second function:

function ofType<T extends ActionWithPayload<string, any>>() {
    return function <K extends one = one>(...param:K[]) : T extends ActionWithPayload<K, infer U> ? U : never{
         return null;
    }
}

var result = ofType<action>()(one.foo); // will be boolean