Cadena de plantilla de TypeScript a partir de objeto anidado
Quiero obtener una unión de todas las claves bajo states en este tipo de objeto,
Tengo un objeto anidado de claves state. Quiero obtener una unión de todas estas claves bajo state en notación de puntos.
Por ejemplo, para esta configuración:
type Config = {
initial: string;
states: {
idle: {
on: {
START: string;
};
};
running: {
on: {
PAUSE: string;
};
};
paused: {
initial: string;
states: {
frozen: {
on: {
HEAT: string;
};
};
};
on: {
RESET: string;
};
};
};
Quiero 'idle' | 'running' | 'paused' | 'paused.frozen'
¿Es esto posible? ¿Alguna idea?
Respuesta #1
Parece que otro trabajo para tipos condicionales recursivos así como para tipos de literales de plantilla:
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> inspecciona T para su propiedad states S, y genera para cada una de sus claves K la unión que queremos, que es K en sí mismo, más la posible concatenación de K con un punto y StatesKeys<S[K]>>. Es decir, estamos concatenando cada clave K con cualquier clave anidada de S[K]. Si no hay claves anidadas, y StatesKeys<S[K]> es never, la literal de plantilla también se convertirá en never, por lo que no tenemos que hacer un caso especial.
Enlace de Playground al código
Respuesta #2
Puedes hacer esto con un tipo condicional recursivo como este:
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"
Área de juego de TypeScript
Ah, me di cuenta tarde de que necesitabas paused.frozen en lugar de solo frozen. Por lo que vale, mi antigua solución podría arreglarse así, usando solo tipos condicionales:
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"
Área de juego de TypeScript
