Reputation: 269
I want to get a union of all the keys under on
in this object type inferred from a configuration object:
type Config = {
initial: string;
states: {
idle: {
on: {
START: string;
};
effect(): void;
};
running: {
on: {
PAUSE: string;
};
effect(): void;
};
paused: {
initial: string;
states: {
frozen: {
on: {
HEAT: string;
};
};
};
on: {
RESET: string;
};
};
};
}
Note that the configuration can have nested on
keys under states
. Right now I can get the first level keys using:
type KeysOfTransition<Obj> = Obj extends Record<PropertyKey, any> ? keyof Obj : never;
type TransitionKeys = KeysOfTransition<Config["states"][keyof Config["states"]]["on"]>
// "START" | "PAUSE" | "RESET"
But I cannot get HEAT
in the union, which is nested. Any ideas?
Upvotes: 4
Views: 3405
Reputation: 327754
There may well be edge cases, but here's an implementation that works for your example:
type KeysUnder<T, K extends PropertyKey> =
T extends object ? {
[P in keyof T]-?: (P extends K ? keyof T[P] : never) | KeysUnder<T[P], K>
}[keyof T] : never;
type ConfigOnKeys = KeysUnder<Config, "on">;
// type ConfigOnKeys = "START" | "PAUSE" | "RESET" | "HEAT"
The idea is to use a recursive conditional type to drill down into object properties. To get KeysUnder<T, K>
, for an object T
, we look at each property key P
. If P
matches K
(which will be "on"
in your use case), then you want the keys of its the property value type T[P]
. The recursive bit is that we also join all these keys to KeysUnder<T[P], K>
.
Upvotes: 8