Search code examples
typescriptgenericstypeskeyoftypeguards

Typescript typeguard on the first generic element of a type


I have an Actions type that need to be mapped to a set of callbacks in the actionMap variable.

However, I'm unsure on how to enforce type of the first generic element of the Action type. Clearly key in keyof Actions isn't doing the trick here. What would I need to use instead?

export interface Action<T, U> {
  type: T,
  data: U,
}

export type Actions =
  | Action<"something", {}>
  | Action<"somethingElse", {}>

const actionMap: { [key in keyof Actions]: (action: Actions) => any } = {
  // I'd like the value of the `type` property of all my actions enforced here. Ex:
  // "something": (action) => {}
  // "somethingElse": (action) => {}
}

If I’m asking the wrong question, what is a better question? I'm not sure I'm using the jargon accordingly.


Solution

  • You can achieve this by using indexed access types to grab a property of the interface, and Extract to narrow down the type of action in the function.

    export interface Action<T, U> {
      type: T,
      data: U,
    }
    
    export type Actions =
      | Action<"something", { something: true}>
      | Action<"somethingElse", { somethingElse: true }>
    
    const actionMap: { [K in Actions['type']]: (action: Extract<Actions, { type: K }>) => any } = {
      "something": (action) => {}, // TS knows action.data.something exists
      "somethingElse": (action) => {} // TS knows action.data.somethingElse exists
    }