Estou tentando conseguir algo assim:
const input = {a: 1, b: "hello"};
const result = renameProperties(input,{a: "alpha", b: "beta"});
result.alpha // number
result.beta // string
result.foo //Property 'foo' does not exist on type...
Então minha função de conversão está assim:
export function renameProperties<
TInput extends Record<string, unknown>,
TConversionMap extends Record<keyof TInput, string>,
TOutput extends // heres where I get stuck
>(input: TInput, conversionMap: TConversionMap): TOutput {
}
Como primeiro passo, tenho:
TOutput extends {[K in TConversionMap[keyof TInput]] : string}
Isso não se comporta exatamente como eu quero - eu esperava que o resultado contivesse apenas chaves convertidas.
result.alpha // string
result.beta // string
result.foo //string - but really was hoping to see an error here
A segunda parte envolveria reter o valor da entrada, eu estava pensando em algo como:
TOutput extends {[K in TConversionMap[keyof TInput]] : TInput[The Input key, if we can retain a reference to it]}
Como eu faria isso? Isso é possível?
Você deseja usar principalmente o remapeamento de chaves em tipos mapeados . Sua
renameProperties()
função deve ser genérica emT
, o tipo de entrada eM
, o tipo de mapeamento de teclas. Então, para cada keyK
inkeyof T
, você cria uma propriedade com keyM[K]
:Aqui você pode ver que
M
foi restringido a algo com as mesmas chavesT
e cujas propriedades são todas semelhantes a chaves (PropertyKey
).Isso lhe dá tudo que você precisa tecnicamente para que isso funcione.
O único problema é que, se você escrever o objeto de mapeamento como um objeto literal para o mapeamento, o TypeScript, por padrão, não acompanhará os tipos literais dos valores das propriedades. Se você escrever
{a: "x", b: "y"}
, o TypeScript irá inferir isso como{a: string, b: string}
, o que não ajuda você. Você gostaria de umaconst
afirmação em{a: "x", b: "y"} as const
vez disso.Uma melhoria é criar
M
umconst
parâmetro de tipo , para que se o argumento de mapeamento for um objeto literal, o TypeScript irá tratá-lo como se aconst
afirmação estivesse lá, para que o chamador possa deixá-lo de fora:Então isso funciona conforme desejado. Observe que se o mapeamento não for passado em linha, você precisará dessa
const
afirmação. Nada pode magicamente fazer isso funcionar para você:Então você iria querer
Link do Playground para o código