unknown, o any que obriga a pensar
O any desliga o TypeScript. Tudo é permitido num valor any: chamar métodos que não existem, passá-lo a funções que esperam outra coisa, ler campos inventados. O erro aparece na mesma — só que em produção.
O unknown é o tipo honesto para "não sei o que isto é": aceita qualquer valor, mas não deixa fazer nada com ele antes de se provar o que é.
1. A diferença
const comAny: any = JSON.parse(textoVindoDaRede);
comAny.utilizador.nome.toUpperCase(); // compila. Rebenta em execução.
const comUnknown: unknown = JSON.parse(textoVindoDaRede);
comUnknown.utilizador;
// ~~~~~~~~~~ Erro: 'comUnknown' é do tipo 'unknown'2. O caso que aparece todos os dias: o catch
Em modo estrito, o erro apanhado num catch é unknown — e é bem que seja, porque em JavaScript pode ser atirado qualquer coisa, incluindo um texto ou um número:
try {
await publicar();
} catch (erro) {
console.log(erro.message);
// ~~~~~~~~~~~~ Erro: 'erro' é do tipo 'unknown'
}try {
await publicar();
} catch (erro) {
// Provar primeiro, usar depois
const mensagem = erro instanceof Error ? erro.message : String(erro);
console.log(mensagem);
}3. Provar o que é, uma vez só
Para dados que chegam de fora, o padrão é uma função que responde a uma pergunta de tipo — repare no valor is Artigo no retorno:
type Artigo = { id: number; titulo: string };
function eArtigo(valor: unknown): valor is Artigo {
return (
typeof valor === 'object' &&
valor !== null &&
'id' in valor &&
typeof valor.id === 'number' &&
'titulo' in valor &&
typeof valor.titulo === 'string'
);
}
const recebido: unknown = JSON.parse(textoVindoDaRede);
if (eArtigo(recebido)) {
console.log(recebido.titulo.trim()); // daqui para baixo é um Artigo
}Em projetos com mais formas para validar, uma biblioteca como o Zod faz isto declarativamente. O princípio é o mesmo: a fronteira entre o mundo e o código tem um sítio só, e é lá que se verifica.