Digamos que você tenha uma função que gera um erro configurado especificamente:
function throwMyError(): never {
throw new Error("My Custom error")
}
E você tem um valor anulável:
declare const str: string | null
Você poderia restringir esse valor a algo não anulável como este:
if(!str) throwMyError()
str.toLocaleLowerCase() // fine
Agora coloque essa chamada de função em um objeto literal e ela não poderá mais fazer o mesmo estreitamento:
const foo = { throwMyError }
if (!str) foo.throwMyError()
str.toLocaleLowerCase() // 'obj' is possibly 'null'.(18047)
Ou:
const bar = {
throwMyError: (): never => {
throw new Error()
}
}
if (!str) bar.throwMyError()
str.toLocaleLowerCase() // 'obj' is possibly 'null'.(18047)
Por que o estreitamento não funciona quando a função de lançamento é chamada a partir de um tipo de objeto?
Isso pode ser resolvido sem criar uma função de predicado de tipo?
Consulte Playground datilografado
Pergunta relacionada: Estreitamento de tipo e nunca funciona
Mas isso não parece resolver esta questão porque em todos os casos o tipo de função é explicitamente anotado para retornarnever
As condições nas quais uma
never
função de retorno é tratada como uma afirmação que afeta a análise do fluxo de controle são descritas no PR de implementação, microsoft/TypeScript#32695 . Se parece com isso:O problema que você está tendo com ambos
foo
ébar
que são variáveis sem uma anotação de tipo explícita . Você permitiu que o compilador inferisse seus tipos, o que em quase todas as outras situações é algo perfeitamente aceitável e razoável de se fazer. Mas aparentemente é muito difícil apoiar isso para funções de asserção enever
funções de retorno; diz-se que causa lentidão no verificador de tipo e avisos de circularidade.Se você quiser
foo
ebar
trabalhar, precisará anotá-los explicitamente, assim:ou
Esse é o comportamento que você queria e, para o código de exemplo aqui, não há muito trabalho extra. Mas, na prática, essa restrição nas anotações de tipo faz com que o uso de métodos
never
de retorno e asserção seja um fardo significativo, pois você precisa criar nomes explícitos para tipos que, de outra forma, seriam convenientemente anônimos e inferidos para você. Dependendo do caso de uso, isso pode variar de um pequeno aborrecimento (const foo: Foo = ⋯
) a um completo problema (const baz: Baz<Map<string, boolean>, "qux", [number, "hello"], Baz<Map<string, Date>, "quux", [boolean, "goodbye"], ⋯>> = ⋯
). Portanto, proceda com cautela.Link do Playground para o código