Dado um objeto:
const obj = { a: 1 } as { a: number } | { b: number }
const key = "a" as string
if (key in obj) {
console.log(obj[key])
// string cannot be used to index { a: number } | { b: number }
}
Como faço para acessar com segurança obj
(sem declaração de tipo)?
Não consigo usar verificações literais
if ("a" in obj) {
// obj.a is number
}
uma vez que o valor de key
não é pré-determinado.
Infelizmente, o tipo de restrição que você está tentando fazer
if (key in obj)
é difícil de acertar. Consulte microsoft/TypeScript#34867 para uma discussão oficial sobre isso.Atualmente, o TypeScript não pode restringir
key
quando você verificakey in obj
; isso é solicitado em microsoft/TypeScript#43284 , mas isso interagiria mal com algumas dasin
restrições de operador existentes que atuam no tipo deobj
.Mesmo se o estreitamento de chave fosse implementado, não funcionaria muito bem quando
obj
fosse do tipo union . Em certo sentido, você está tentando restringir amboskey
eobj
ao mesmo tempo, de alguma forma correlacionada, de modo que issokey in obj
implique que o par[key, obj]
é de um tipo como["a", {a: number}] | ["b", {b: number}]
.Mas não há facilidade no TypeScript para lidar com essas coisas; consulte microsoft/TypeScript#30581 para obter o problema geral com uniões correlacionadas; muitas vezes, eles podem ser refatorados para usar genéricos, conforme descrito em microsoft/TypeScript#47109 , mas, neste caso, não valeria nem remotamente o esforço.
Provavelmente, minha recomendação aqui é que você deve ampliar o tipo de
obj
para algo mais passível de indexação com uma chave desconhecida. Talvez com uma assinatura de índice comoIsso representa a ideia de que qualquer propriedade será
number
ou ausente/undefined
, e você pode apenas indexar diretamente (semin
verificar) e comparar comundefined
:Link do Playground para o código
Uma maneira divertida de fazer isso é
mas se você estiver com pressa também pode fazer
Nota1: Tomei a liberdade de mudar
{a:number}|{b:number}
para{a?:number,b?:number}
porque a maneira como o TypeScript avalia{a:number}|{b:number}
umnever
tipo, que provavelmente não é o que você pretendia.Observação2: também usei
declare const
formulários, em vez de valores constantes explícitos, para que fique claro que a chave precisa ser verificada antes de ser usada para acessar o objeto. (Caso contrário, a questão é trivial).Atualizar
Nesta versão o TypeScript infere o tipo de
obj
to be{a: number;b?: undefined;}|{b: number;a?: undefined;}
, que é reduzido a{a?: number;b?: undefined;}
. É somente nesta forma condicional especial de uma linha que o TypeScript irá gerar uma união de tipos sem chaves comuns. Geralmente, se você deseja uma "união" conceitual de tipos com chaves diferentes, pode fazer algo comoMas as chaves são todas opcionais - isso é uma limitação.