4

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?

1
  • Is the second lv on always under the second lv states? Commented Mar 26, 2021 at 3:03

1 Answer 1

8

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

Sign up to request clarification or add additional context in comments.

4 Comments

This works perfectly - I'm getting a "Type instantiation is excessively deep and possibly infinite." when feeding ConfigOnKeys to other functions, but I can figure it out.
Yes, recursive conditional types do have issues, and possible infinite regress is one of them. There are techniques to try to prevent that from happening, but without a minimal reproducible example it's hard to know how to advise.
Just out of curiosity, should not TS be able to infer the type automatically?
Infer what type automatically where? This is a 3-year-old answer and I don't see anywhere in this q/a where inference would be happening. I'd ask you to clarify but this probably isn't the place for it. You might want to open your own question post if you have a question.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.