TypeScript Tipos Intermediário
Narrowing
Narrowing (estreitamento de tipos) é o processo pelo qual o TypeScript refina o tipo de uma variável dentro de um bloco de código, com base em verificações feitas em tempo de execução. Isso permite acessar propriedades e métodos específicos de um tipo sem precisar de casts manuais, mantendo a segurança de tipos.
O compilador usa análise de fluxo para acompanhar como o tipo de uma variável muda ao longo do código. Verificações como typeof, instanceof, o operador in, comparações de igualdade e até mesmo asserções de tipo influenciam esse fluxo. Por exemplo, dentro de um if (typeof x === 'string'), o TypeScript sabe que x é string naquele bloco.
Além disso, o narrowing funciona com uniões discriminadas, onde uma propriedade literal comum (como kind) permite distinguir entre variantes de um tipo. Isso é muito usado em padrões de projeto como Redux actions ou resultados de API.
Neste card, vamos explorar os principais mecanismos de narrowing e como aplicá-los para escrever código mais seguro e expressivo.
Pontos-chave
typeofestreita tipos primitivos comostring,number,boolean,symbol,bigint,undefined,objectefunction.instanceofestreita para instâncias de classes, verificando o protótipo.- O operador
inverifica se uma propriedade existe em um objeto, estreitando uniões de tipos de objeto. - Comparações de igualdade (
===,!==) com literais ou outros valores podem estreitar o tipo. - A análise de fluxo do TypeScript rastreia o tipo ao longo do código, inclusive após retornos e em blocos condicionais.
// Exemplo de narrowing com typeof, instanceof, in e igualdade
class Cachorro {
latir() { return 'Woof!'; }
}
class Gato {
miar() { return 'Miau!'; }
}
type Animal = Cachorro | Gato;
function emitirSom(animal: Animal): string {
if (animal instanceof Cachorro) {
return animal.latir(); // animal é Cachorro
} else {
return animal.miar(); // animal é Gato
}
}
// Narrowing com typeof
function formatar(valor: string | number): string {
if (typeof valor === 'string') {
return valor.toUpperCase();
}
return valor.toFixed(2);
}
// Narrowing com in
interface Peixe { nadar(): void }
interface Passaro { voar(): void }
function mover(animal: Peixe | Passaro) {
if ('nadar' in animal) {
animal.nadar();
} else {
animal.voar();
}
}
// Narrowing com igualdade
type Status = 'ativo' | 'inativo';
function mostrarStatus(s: Status) {
if (s === 'ativo') {
console.log('Usuário ativo');
} else {
console.log('Usuário inativo');
}
}
// Testando
console.log(emitirSom(new Cachorro())); // Woof!
console.log(formatar(42)); // 42.00
console.log(formatar('typescript')); // TYPESCRIPT
mover({ nadar: () => console.log('Nadando') }); // Nadando
mostrarStatus('ativo'); // Usuário ativo