Lições do módulo (1/2)
Tipo Guardas
O estreitamento de tipo é um dos conceitos mais importantes do TypeScript. Muitas vezes, porém, proteções de tipo padrão como typeof ou instanceof não são suficientes para objetos personalizados complexos. É aqui que entram os Protetores de tipo personalizado.
Protetores de tipo padrão
Vamos começar resumindo como o TypeScript restringe os tipos usando operadores JavaScript padrão:
function processInput(val: string | number) {
if (typeof val === 'string') {
// Here 'val' is of type string
console.log(val.toUpperCase());
} else {
// Here 'val' is of type number
console.log(val.toFixed(2));
}
}Protetores de tipo personalizados (operador is)
Para definir uma proteção de tipo personalizada, criamos uma função que retorna um predicado de tipo no formato parameterName is Type em vez de um tipo de retorno booleano padrão.
A função deve retornar um booleano (true ou false). Se retornar true, o TypeScript saberá que o parâmetro passado é do tipo especificado.
interface Cat {
name: string;
meow(): void;
}
interface Dog {
name: string;
bark(): void;
}
// This is a custom type guard
function isCat(animal: Cat | Dog): animal is Cat {
return (animal as Cat).meow !== undefined;
}
function makeNoise(pet: Cat | Dog) {
if (isCat(pet)) {
// Here TypeScript knows 'pet' is a Cat
pet.meow();
} else {
// Here TypeScript knows 'pet' is a Dog
pet.bark();
}
}Segurança e fundição (as vs protetores de tipo)
A conversão de tipo (por exemplo, val as Cat) força o compilador a confiar no desenvolvedor sem qualquer verificação real em tempo de execução. Se o objeto de tempo de execução não atender às expectativas, o código falhará silenciosamente ou lançará exceções.
Os protetores de tipo personalizados, por outro lado, realizam uma verificação de tempo de execução robusta e dinâmica, informando com segurança e precisão ao compilador TypeScript o tipo real da variável:
function processInput(input: unknown) {
// unsafe casting: could crash if input does not have the split method
// const str = input as string;
// console.log(str.split(' '));
// safe guard
if (isString(input)) {
console.log(input.split(' ')); // 100% Safe!
}
}Experimente você mesmo
Exercício 1: Guarda Tipo String
Crie uma função simples chamada isString que atue como um protetor de tipo personalizado para verificar se um valor desconhecido é uma string.
Mostrar dica
O tipo de retorno da função deve ser val is string, e você deve usar typeof para verificar se é igual a ‘string’.
Solução disponível após 3 tentativas
Exercício 2: Proteção de Tipo de Usuário Premium
Dados os tipos User e PremiumUser, escreva uma função de proteção de tipo isPremiumUser(user: User): user is PremiumUser que verifica se o usuário tem a função 'premium'.
Mostrar dica
Defina a assinatura como função isPremiumUser(user: User): user is PremiumUser e compare user.role com 'premium'.
Solução disponível após 3 tentativas
Exercício 3: Validação de objeto de erro personalizado
Implemente a função isCustomError(err: desconhecido): err is CustomError que verifica se um valor desconhecido é um objeto CustomError não nulo contendo a chave 'código'.
Mostrar dica
Primeiro verifique se typeof err é 'objeto' e não nulo e, em seguida, use o operador 'in' para verificar a propriedade 'código'.
Solução disponível após 3 tentativas
Exercício 4: Admin Type Guard
Dadas duas interfaces User (com propriedade role: string) e Admin (estendendo User e tendo uma propriedade adminToken: string), escreva uma função de proteção de tipo chamada isAdmin que aceita um usuário do tipo User e retorna um predicado de tipo user is Admin. O guarda deve verificar se a função do usuário é igual a ‘admin’.
Mostrar dica
Defina a assinatura como função isAdmin(user: User): user é Admin e verifique se user.role === 'admin'.
Solução disponível após 3 tentativas