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?
CodePudding user response:
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];
// }