how to do case statement with generics in typescript

It is possible to use generics along with conditional types to create case statements in TypeScript. Here is an example:

index.ts
type CaseType<T, Cases extends { [key: string]: (...args: any[]) => any }> = {
  [K in keyof Cases]: (value: T extends K ? Parameters<Cases[K]>[0] : never) => ReturnType<Cases[K]>
}[keyof Cases];

// example usage
type Color = 'red' | 'green' | 'blue'

type ColorAction = { type: 'colorChanged', color: Color }

type ActionCases = {
  'colorChanged': (color: Color) => void;
  'otherAction': () => void;
}

type ActionFn = CaseType<ColorAction['type'], ActionCases>;

const handleAction: ActionFn = (value: any) => {
  // only allows parameters and return types based on the type of 'type' field in the action object
  switch (value.type) {
    case 'colorChanged': {
      const { color } = value;
      // handle color change
      break;
    }
    case 'otherAction': {
      // handle other action
      break;
    }
  }
};
828 chars
31 lines

In this example, we define a generic CaseType that takes in two parameters: T, which represents the type of the object field that is being matched on, and Cases, which is an object that maps field names to functions that handle those fields. The Cases object must have a key for every possible T value.

We use a conditional type to create an array of all possible Cases functions, and then index into that array with the matched T value to get the correct function signature. The resulting function signature matches the signature of the Cases function for that specific T value.

We then define an example usage by defining a sample ColorAction object, along with a Cases object that matches the possible type values of ColorAction. Finally, we use the ActionFn type, which is inferred to be the correct function signature for the given type field value, and define a handleAction function that can handle any ColorAction object based on its type field value.

gistlibby LogSnag