Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[PT-BR] Translate Conditional Types and Unknown and Never #964

Merged
merged 2 commits into from
Aug 28, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// Tipos Condicionais fornecem uma maneira simples de adicionar
// lógica no sistema de tipos do Typescript. Esse é um recurso
// avançado, e é muito possível que você não precise utilizá-lo
// no seu código do dia a dia.

// Um tipo condicional se parece com:
//
// A extends B ? C : D
//
// Onde a condição é: se um tipo se extende a uma expressão,
// e que tipo deveria ser retornado.

// Vamos passar por alguns exemplos, por questões de brevidade
// usaremos apenas uma letra para tipos genéricos. Isso é opcional,
// mas nos restringimos à 60 caracteres para caber na tela.

type Gato = { miau: true };
type Cachorro = { latido: true };
type Guepardo = { miau: true; rapido: true };
type Lobo = { latido: true; uivos: true };

// Podemos criar um tipo condicional onde é possível extrair
// tipos que se aplicam apenas com algo que late.

type ExtrairLatidos<A> = A extends { latido: true } ? A : never;

// Assim podemos criar tipos envolvidos pelo ExtrairLatidos:

// Um gato não late, então iremos retornar never
type GatoNever = ExtrairLatidos<Gato>;
// Um lobo late, então retornaremos a forma de lobo
type TipoLobo = ExtrairLatidos<Lobo>;

// Isso se torna útil quando você quer trabalhar com uma união
// de vários tipos e reduzir o número de potenciais opções:

type Animais = Gato | Cachorro | Guepardo | Lobo;

// Quando você aplica ExtrairLatidos para um tipo de união,
// é o mesmo que testar a condição com todos os membros do tipo:

type Latido = ExtrairLatidos<Animais>;

// = ExtrairLatidos<Gato> | ExtrairLatidos<Cachorro> |
// ExtrairLatidos<Guepardo> | ExtrairLatidos<Lobo>
//
// = never | Cachorro | never | Lobo
//
// = Cachorro | Lobo (veja example:unknown-and-never)

// Isso é chamado de tipo condicional distributivo porque
// o tipo distribui para cada membro da união.

// Tipos condicionais diferidos

// Tipos condicionais podem ser usados para diminuir suas APIs
// que podem retornar diferentes tipos dependendo dos inputs.

// Por exemplo, essa função pode retornar tanto uma string
// quanto um number dependendo do boolean passado.

declare function pegarID<T extends boolean>(legal: T): T extends true ? string : number;

// Então dependendo do quanto o sistema de tipos sabe sobre o boolean
// você irá receber um tipo de retorno diferente:
let retornoDeString = pegarID(true);
let retornoDeNumber = pegarID(false);
let stringOuNumber = pegarID(Math.random() < 0.5);

// Nesse caso acima o TypeScript sabe o tipo de retorno imediatamente.
// Contudo, você pode usar tipos condicionais em funções
// onde o tipo não é conhecido. Isso é chamado tipo condicional diferido.

// O mesmo que o nosso ExtrairLatidos acima, mas como uma função
declare function extrairMiado<T>(x: T): T extends { miau: true } ? T : undefined;

// Existe uma ferramenta muito útil dentro dos tipos condicionais, na qual
// é possível especificamente dizer ao TypeScript que ele deve inferir o tipo
// quando diferido. Essa é a palavra chave 'infer'.

// 'infer' é normalmente usado para criar metatipos que inspecionam
// os tipos existentes no seu código, pense nisso como a criação de uma
// nova variável dentro do tipo.

type PegarOTipoDoRetorno<T> = T extends (...args: any[]) => infer R ? R : T;

// Brevemente:
//
// - esse é um tipo genérico condicional chamado PegarOTipoDoRetorno
// que recebe um tipo como primeiro parâmetro
//
// - a condição checa se o tipo é uma função, e se for cria um novo tipo
// chamado R baseado no retorno do valor da função
//
// - se a checagem passar, o valor do tipo é inferido como o valor do
// retorno da função, caso contrario é o tipo original
//

type retornoDoPegarID = PegarOTipoDoRetorno<typeof pegarID>;

// Isso falha na verifição de ser uma função, e iria apenas retornar o tipo
// passado a ele.
type pegarGato = PegarOTipoDoRetorno<Gato>;
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
// Unknown

// Unknown é um daqueles tipos que, assim que você entende,
// acha muitos casos de uso. Ele age como um irmão para o tipo any.
// any permite ambiguidade - unknown requer especificidades.

// Um bom exemplo poderia envolver um JSON parser. Dados JSON podem vir
// de diferentes formas e o criador da função que analisa o JSON não
// saberá a forma do dado - a pessoa chamando a função deve.

const transformadorJson = (stringJson: string) => JSON.parse(stringJson);

const minhaConta = transformadorJson(`{ "nome": "Dorothea" }`);

minhaConta.nome;
minhaConta.email;

// Se você passar o mouse em transformadorJson, poderá ver que ele tem
// o tipo de retorno any, assim como o minhaConta. É possível ajustar isso
// com tipos Genéricos - mas também é possível ajustar isso com o unkown.

const transformadorJsonUnkown = (stringJson: string): unknown => JSON.parse(stringJson);

const minhaOutraConta = transformadorJsonUnkown(`{ "nome": "Samuel" }`);

minhaOutraConta.nome;

// O objeto minhaOutraConta não pode ser usado até o tipo ser declarado
// para o TypeScript. Isso pode ser usado para garantir que
// quem consumir a API pense em sua tipagem com antecedência:

type Usuario = { nome: string };
const minhaContaDeUsuario = transformadorJsonUnkown(`{ "nome": "Samuel" }`) as Usuario;
minhaContaDeUsuario.nome;

// Unknown é uma boa ferramenta, para entendê-lo ainda mais leia:
// https://mariusschulz.com/blog/the-unknown-type-in-typescript
// https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-0.html#new-unknown-top-type

// Never

// Como o Typescript suporta análise de fluxo do código, a linguagem
// precisa ser capaz de representar quando lógicamente o código não pode
// acontecer. Por exemplo, essa função não pode retornar:

const nuncaRetorna = () => {
// Se for lançado um erro na primeira linha
throw new Error("Sempre lança um erro, nunca retorna");
};

// Se você passar o mouse em cima da função, verá () => never
// que significa que isso nunca deverá acontecer. Estes ainda
// podem ser passados como outros valores:

const meuValor = nuncaRetorna();

// Tendo uma função que retorna never pode ser útil quando lidamos com
// a imprevisiblidade do runtime JavaScript e os
// consumidores da API que podem não estar usando tipos:

const validarUsuario = (usuario: Usuario) => {
if (usuario) {
return usuario.nome !== "NaN";
}

// De acordo com o sistema de tipos, esse caminho do código
// nunca deveria acontecer, que combina com o tipo de
// retorno do nuncaRetorna.

return nuncaRetorna();
};

// O estado da definição de tipos indica que um usuário deve ser
// passado, mas existem muitas válvulas de escape no JavaScript
// por meio das quais você não pode garantir isso.

// Usar uma função que retorna never te permite adicionar
// código em partes que não deveriam ser possíveis.
// Isso é útil para mostrar uma mensagem de erro melhor,
// ou fechar recursos como arquivos ou loops.

// Um tipo de uso bem popular para o never é garantir que um
// switch é exaustivo. Por exemplo, todos os caminhos são cobertos.

// Aqui tem um enum e um switch exaustivo, tente adicionar
// uma nova opção para o enum (talvez Tulipa?)

enum Flor {
Rosa,
Rododendro,
Violeta,
Margarida,
}

const nomeDaFlorEmLatim = (flor: Flor) => {
switch (flor) {
case Flor.Rosa:
return "Rosa rubiginosa";
case Flor.Rododendro:
return "Rhododendron ferrugineum";
case Flor.Violeta:
return "Viola reichenbachiana";
case Flor.Margarida:
return "Bellis perennis";

default:
const _checagemExaustiva: never = flor;
return _checagemExaustiva;
}
};

// Você vai ter um erro de compilação falando que seu
// novo tipo de flor não pode ser convertido em never.

// Never em Unions

// O never é removido automaticamente do tipo union.

type NeverERemovido = string | never | number;

// Se você olhar no tipo para NeverERemovido, verá que
// é string | number. Isso é porque never nunca deve acontecer em
// tempo de execução, já que você não pode atribuir a ele.

// Esse recurso é muito utilizado em example:conditional-types