🧙♂️ Aprenda Conditional Types no TypeScript
Se você quer levar seu conhecimento de TypeScript para o próximo nível, você precisa entender sobre Conditional Types (Tipos Condicionais). Aprender isso com certeza irá te colocar na frente da maioria dos desenvolvedores Typescript, já que poucos conhecem sobre.
Antes de tudo: Conceitos fundamentais
Para entender os Conditional Types, é necessário dominar três conceitos do TypeScript:
- Generics
extends
- Ternário
1. Generics
Leia esse outro artigo que fiz caso você não saiba o que é:
2. Extends
Usado para impor que um tipo herda (ou estende) outro tipo.
interface A {
name: string;
}
// B tem todas as propriedades de A
interface B extends A {
age: number;
}
Nos Conditional Types, o extends
é usado como parte de uma condição (não é a mesma funcionalidade do examplo, mas você irá entender mais pra frente)
3. Operador Ternário
Muito conhecido de outras linguagens, o ternário permite retornar valores diferentes baseado em uma condição:
condição ? valorSeVerdadeiro : valorSeFalso
Esses três elementos formam a base dos Conditional Types.
O que são Conditional Types?
Um Conditional Type permite criar um tipo baseado em uma condição lógica entre dois tipos.
A base dos Conditional Types:
Usar um Conditional Types, nada mais é do que você colocar uma verificação no seu tipo, onde você usa extends
para verificar e usa o ternário para retornar o tipo dependendo da verificação.
type Teste<T> = T extends string ? true : false;
type A = Teste<string>; // true
type B = Teste<number>; // false
Aqui, o tipo Teste
verifica se T
é uma string. Se sim, retorna true
, senão false
.
Para simplificar, você pode imaginar o seguinte:
T extends string
=>T === string
... ? true : false;
=>if(...) { return true } else { return false }
A syntax pode parecer complicada, mas você pode imaginar como se fosse vários if
e else
.
Usos práticos com Conditional Types
1. Remover tipos null ou undefined
O TypeScript já possui o tipo NonNullable
, mas ele pode ser recriado usando Conditional Types:
type NonNullableCustom<T> = T extends null | undefined ? never : T;
type A = NonNullableCustom<string | null>; // string
Nesse caso, para "excluir" um tipo, você retorna o tipo never
.
2. Recriando o tipo Extract
O TypeScript já possui o tipo Extract
, mas ele pode ser recriado usando Conditional Types:
type ExtractCustom<T, U> = T extends U ? T : never;
Por exemplo:
type A = ExtractCustom<string | number, string>; // string
Nesse caso, você pode ver como é possível passar vários parâmetros genéricos e usa-los nas condições.
Recursividade com Conditional Types
Como exemplo, suponha que você quer extrair qual é o tipo de um array. No Typescript, você pode usar [number]
para accessar o tipo do array.
type ArrayValue<T extends any[]> = T[number];
type A = ArrayValue<string[]>; // => string
// Mesma coisa que fazer "string[][number]" => string
Mas esse exemplo tem um problema, porque ele não funciona para arrays de multiplas dimensões:
type ArrayValue<T extends any[]> = T[number];
type A = ArrayValue<string[][]>; // => string[], mas era para ser apenas string
E é ai que entra o Conditional Types para resolver esse problema.
Porque no Conditional Types, você pode chamar o próprio tipo recursivamente
type DeepArrayValue<T> = T extends any[] // verifica se é um array qualquer
? DeepArrayValue<T[number]> // extrai o tipo do array e passa de novo para o próprio tipo
: T; // retorna o tipo quando ele não é mais um array
type A = DeepArrayValue<string[][][]>; // string
Você poderia "traduzir" o código a cima como se fosse um if
e else
da seguinte forma:
function DeepArrayValue(T) {
if(T === qualquer_array) { // verifica se é um array qualquer
const tipo_do_array = T[number] // extrai o tipo do array
return DeepArrayValue(tipo_do_array) // chama de novo a função com o tipo extraido
} else {
return T // retorna o tipo quando ele não é mais um array qualquer
}
}
Para acabar com a recursividade, você acaba retornando o próprio T
quando ele não é mais um "array qualquer".
Considerações Finais
Conditional Types é extremamente poderosos no TypeScript e com ele foi criado a maioria dos tipos já existentes (como o NonNullable
e Extract
que vimos anteriormente)
Não vai ser todos os dias que vai criar um Conditional Types do zero, mas pode ter certeza que você vai usar bastante algum tipo já existente do Typescript que usa ele e por isso é sempre bom entender como funciona o Conditional Types.
Esse artigo foi baseado no meu vídeo: Apenas 1% dos devs sabem disso no TypeScript
Se tiver dúvidas ou sugestões, deixe nos comentários!
Fonte: https://youtu.be/PO0F5yxrcEk