Zoeken op website

TypeScript-sjabloonletterreeks uit genest object


Ik wil een vereniging krijgen van alle sleutels onder states in dit objecttype,

Ik heb een genest object van state-sleutels. Ik wil een unie krijgen van al deze sleutels onder state in dotnotatie.

Bijvoorbeeld, voor deze configuratie:

type Config = {
initial: string;
states: {
    idle: {
        on: {
          START: string;
        };
    };
    running: {
        on: {
            PAUSE: string;
        };
    };
    paused: {
        initial: string;
        states: {
            frozen: {
                on: {
                    HEAT: string;
                };
            };
        };
        on: {
          RESET: string;
        };
    };
};

Ik wil 'idle' | 'running' | 'paused' | 'paused.frozen'

Is dit mogelijk? Ideeën?

Antwoord #1

Lijkt ook een klus voor recursieve conditionele types en template literal types:

type StatesKeys<T> = T extends { states: infer S } ? {
  [K in Extract<keyof S, string>]: K | `${K}.${StatesKeys<S[K]>}`
}[Extract<keyof S, string>] : never

type ConfigStatesKeys = StatesKeys<Config>;
// type ConfigStatesKeys = "idle" | "running" | "paused" | "paused.frozen"

StatesKeys<T> inspecteert T op zijn states eigenschap S, en genereert voor elk van zijn sleutels K de vereniging die we willen, namelijk K zelf, plus de mogelijke concatenatie van K met een punt en StatesKeys<S[K]>>. Dat wil zeggen, we concatenateren elke sleutel K met eventuele geneste sleutels van S[K]. Als er geen geneste sleutels zijn, en StatesKeys<S[K]> never is, zal de template literal ook never worden, dus hoeven we hier geen speciale gevallen voor te maken.

Speeltuinlink naar code

Antwoord #2

Je kunt dit doen met een recursief conditioneel type zoals dit:

type StateKeys<T> = T extends {states: infer S}
  ? keyof S | StateKeys<S[keyof S]>
  : never

type Test = StateKeys<Config>
// type Test = "idle" | "running" | "paused" | "frozen"

TypeScript-speeltuin

Ah, ik had gemist dat je paused.frozen nodig had in plaats van alleen frozen. Voor wat het waard is, mijn oude oplossing kon op deze manier worden aangepast, met alleen voorwaardelijke types:

type StateKeysForKey<S, K> = K extends keyof S & string
  ? `${K}.${StateKeys<S[K]>}`
  : never

type StateKeys<T> = T extends {states: infer S}
  ? keyof S | StateKeysForKey<S, keyof S>
  : never

type Test = StateKeys<Config>
// type Test = "idle" | "running" | "paused" | "paused.frozen"

TypeScript-speeltuin

Gerelateerde artikelen