Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TypeScript keyof of nested object type

Tags:

typescript

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?

like image 786
cassiozen Avatar asked Oct 20 '25 13:10

cassiozen


1 Answers

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>.

Playground link to code

like image 180
jcalz Avatar answered Oct 23 '25 07:10

jcalz



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!