Conditional Types
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.
// Podemos criar um tipo condicional onde é possível extrair
tipos que se aplicam apenas com algo que late.
type Gato = { miau: true };
type Cachorro = { latido: true };
type Guepardo = { miau: true; rapido: true };
type Lobo = { latido: true; uivos: true };
// 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 ExtrairLatidos = 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
// Quando você aplica ExtrairLatidos para um tipo de união,
é o mesmo que testar a condição com todos os membros do tipo:
type Animais = Gato | Cachorro | Guepardo | Lobo;
// = ExtrairLatidos
type Latido = ExtrairLatidos
// Então dependendo do quanto o sistema de tipos sabe sobre o boolean
você irá receber um tipo de retorno diferente:
declare function pegarID
// 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
let retornoDeString = pegarID(true);
let retornoDeNumber = pegarID(false);
let stringOuNumber = pegarID(Math.random() < 0.5);
// 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.
declare function extrairMiado
// 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 PegarOTipoDoRetorno
// Isso falha na verifição de ser uma função, e iria apenas retornar o tipo
passado a ele.
type retornoDoPegarID = PegarOTipoDoRetorno
type pegarGato = PegarOTipoDoRetorno