0

I'm trying to infer the parameters of a nested object that looks like this:

const lightSwitch = {
  off: {
    switchOn: (state: Data, payload: number) => ({
      ...state,
      state: 'on',
      meta: payload,
    }),
  },
  on: {
    switchOff: (state: Data) => ({
      ...state,
      state: 'off',
    }),
  },
};

I want a type, Params, that looks like this:

 {
   'switchOn': [state: Data, payload: number]
   'switchOff': [state: Data]
 }

Here's what I'm doing:

type Intersect<T> = T extends { [K in keyof T]: infer E } ? E : T;

type Flattened = Intersect<typeof lightSwitch>;

type Params = { [K in keyof Flattened]: Parameters<Flattened[K]> }; // type Params = {}

Why is Params empty, and how do I correct this?

Playground

Tobias S.
  • 21,159
  • 4
  • 27
  • 45
Andy Jessop
  • 225
  • 1
  • 13

1 Answers1

1

Flattened is a union of objects which do not share any properties. That's why keyof Flattened just evaluates to never resulting in an empty object type. We should convert this union to an intersection using the UnionToIntersection type.

type UnionToIntersection<U> = 
  (U extends any ? (k: U)=>void : never) extends ((k: infer I)=>void) ? I : never

type GetNested<T> = UnionToIntersection<T[keyof T]>

The GetNested type will convert typeof lightSwitch to an object with two properties switchOn and switchOff which can be mapped.

type Params<T> = { 
  [K in keyof GetNested<T>]: 
    Parameters<
      GetNested<T>[K] extends infer U extends (...args: any) => any ? U : never
    > 
};

type T0 = Params<typeof lightSwitch>
// type T0 = {
//   switchOn: [state: Data, payload: number];
//   switchOff: [state: Data];
// }

Playground

Tobias S.
  • 21,159
  • 4
  • 27
  • 45
  • 1
    `UnionToIntersection` looks like a **very** useful type. – T.J. Crowder Aug 10 '22 at 13:59
  • The only issue with this method is that it doesn't work if trying to get the parameters of a generic object, e.g.: `type Params = { [K in keyof GetNested]: Parameters[K]> };` This gives the error: `Type 'UnionToIntersection[K]' does not satisfy the constraint '(...args: any) => any'` – Andy Jessop Aug 10 '22 at 14:41
  • 1
    @AndyJessop updated my answer to account for that – Tobias S. Aug 10 '22 at 14:48