Home > Net >  How can i make a generic keyof type to become a union type of distinct keys
How can i make a generic keyof type to become a union type of distinct keys

Time:12-25

I don't have the right typescript vocabulary to describe what i am trying to achieve, so i will be using my code snippet to describe what i am aiming at. I have two interfaces ActionPayloadTypes and Action

interface ActionPayloadTypes {
    FETCH_LIST_REQUEST: any,
    FETCH_LIST_SUCCESS: string[],
    FETCH_LIST_FAILURE: string
}

interface Action <T extends keyof ActionPayloadTypes> {
  type: T
  payload: ActionPayloadTypes[T]
}

and I'm wanting to create a union type of Actions based on these 2 interfaces.

Basically i want

type Actions = Action<'FETCH_LIST_REQUEST'> | Action<'FETCH_LIST_SUCCESS'> | Action<'FETCH_LIST_FAILURE'>

without having to write the union types manually for each key. I tried using <keyof ActionPayloadTypes> it creates a union type in the Action. See below.

Action<'FETCH_LIST_REQUEST' | 'FETCH_LIST_SUCCESS' | 'FETCH_LIST_FAILURE'>

What am i doing wrong here?

CodePudding user response:

type Actions = {
    [A in keyof ActionPayloadTypes]: Action<A>
}[keyof ActionPayloadTypes];

You have to map each key of the ActionPayloadTypes type into a "call" to the Action type. Then you index this partial result with the keys of ActionPayloadTypes to get the desired union.

CodePudding user response:

You're on the right path. As you noted Action<keyof ActionPayloadTypes> gives you a type where the generic parameter is the union of keys.

In order to get a union with each type of Action I break it into two steps:

First I use a mapped type to get a map with values for each type of Action

    type ActionMap = { [K in keyof ActionPayloadTypes]: Action<K> }
    // {
    //     FETCH_LIST_REQUEST: Action<"FETCH_LIST_REQUEST">;
    //     FETCH_LIST_SUCCESS: Action<"FETCH_LIST_SUCCESS">;
    //     FETCH_LIST_FAILURE: Action<"FETCH_LIST_FAILURE">;
    // }

From here, I can create a union type based on the values in this map

    type ActionUnion = ActionMap[keyof ActionMap];
    // Action<"FETCH_LIST_REQUEST"> | Action<"FETCH_LIST_SUCCESS"> | 
    // Action<"FETCH_LIST_FAILURE">

Of course, you can combine these into a single type to be more succinct

    type ActionUnion = { 
        [K in keyof ActionPayloadTypes]: Action<K> 
    }[keyof ActionPayloadTypes];
    // Action<"FETCH_LIST_REQUEST"> | Action<"FETCH_LIST_SUCCESS"> | 
    // Action<"FETCH_LIST_FAILURE">
  • Related