typescript - Objeto con el valor de una clave dependiente del valor de otra clave
¿Cuál sería la mejor manera de lograr este efecto (nota: esto no es una sintaxis válida - considérelo un pseudocódigo):
type Config = {
[key: string]: <T> {
params: T,
doSth: (params: T) => void,
},
}
Es decir, el tipo genérico T es diferente para el valor de cada clave (que es un objeto en sí mismo), pero al mismo tiempo se está reutilizando dentro de ese valor (objeto) bajo diferentes campos. Además, T se deriva del campo params. Con eso podría hacer:
const config: Config = {
a: {
params: { x: 123 },
doSth: params => {}, // Type of "params" is "{ x: 123 }"
},
b: {
params: { y: 'asd' },
doSth: params => {}, // Type of "params" is "{ y: 'asd' }"
},
}
Para mi sorpresa, buscar esto en Google no arroja resultados coincidentes, aunque parece un patrón relativamente útil: hay muchos problemas similares pero no realmente iguales. Algunas soluciones a esos, que intenté aplicar, todas fallaron.
Respuesta #1
Hay algunas cosas que no se pueden hacer en TypeScript solo con tipos, a veces necesitas una función para ayudar, incluso si la función en realidad no hace nada en tiempo de ejecución. Creo que esta es una de esas ocasiones. Aquí hay un ejemplo de una función que se acerca a lo que estás describiendo:
type ConfigEntry<T extends object> = {
params: T;
doSth: (params: T) => void;
};
function makeConfigEntry<T extends object>(obj: ConfigEntry<T>): ConfigEntry<T> {
return obj;
}
Observa cómo ConfigEntry nos permite relacionar los dos lugares en los que usaremos ese tipo. Usarías esa función al definir los valores de las propiedades:
const config = {
a: makeConfigEntry({
params: { x: 123 },
doSth: params => {}, // Type of `params` is `{ x: number; }`
// ^?
}),
b: makeConfigEntry({
params: { y: "asd" },
doSth: params => {}, // Type of `params` is `{ y: string; }`
// ^?
}),
};
Nota que no hay una anotación de tipo Config en eso. TypeScript lo inferirá a partir del inicializador del objeto.
Pero nota que los tipos en params no son exactamente lo que querías. TypeScript no inferirá los tipos { x: 123; } y { y: "asd"; } de tus objetos. Inferirá ya sea { x: number; } y { y: string; }, o si defines los objetos as const, inferirá { readonly x: 123; } y { readonly y: "asd"; }:
const config = {
a: makeConfigEntry({
params: { x: 123 } as const,
doSth: params => {}, // Type of `params` is `{ readonly x: 123; }`
// ^?
}),
b: makeConfigEntry({
params: { y: "asd" } as const,
doSth: params => {}, // Type of `params` is `{ readonly y: "asd"; }`
// ^?
}),
};
Es probable que esto no sea exactamente lo que querías, pero creo que está cerca, probablemente una de varias aproximaciones que están tan cerca de lo que quieres como se puede lograr con el TypeScript actual.
Ejemplo de patio de recreo de lo anterior
