Tipos condicionais no TypeScript: exemplos práticos e uso avançado
Tags: typescript, braziliandevs, javascript
Published: 2023-04-18 07:57:18

Com os tipos condicionais, um recurso poderoso do TypeScript, é possível criar _tipos que dependem de outros tipos_. Os tipos condicionais são avaliados em tempo de compilação e são definidos usando um operador ternário que especifica uma condição, e o ponto de interrogação indica que o tipo é condicional.

A sintaxe para um tipo condicional é a seguinte:
```typescript
T extends U ? X : Y
```

Aqui, `T` e `U` são tipos, `X` e `Y` são tipos ou expressões de tipo, e `extends` é uma palavra-chave que especifica uma restrição em `T`.

Um caso de uso comum para tipos condicionais é extrair propriedades de objetos. Por exemplo, considere a seguinte interface:

```typescript
interface User {
  id: number;
  name: string;
  email: string;
}
```

Podemos criar um tipo que extrai as chaves de `User` da seguinte forma:
```typescript
type UserKeys = keyof User; // "id" | "name" | "email"
```

Usando um tipo condicional, podemos criar uma versão mais flexível de `UserKeys` que pode extrair chaves de qualquer tipo de objeto:
```typescript
type KeysOfType<T, U> = {
  [K in keyof T]: T[K] extends U ? K : never;
}[keyof T];
```

Aqui, `KeysOfType` recebe dois parâmetros de tipo, `T` e `U`, e retorna uma união de chaves de `T` cujos valores são do tipo `U`. Por exemplo:
```typescript
interface Car {
  make: string;
  model: string;
  year: number;
}

type StringKeys = KeysOfType<Car, string>; // "make" | "model"
```

Esse tipo retorna "make" e "model", pois essas são as chaves de Car cujos valores são do tipo `string`.

Outro caso de uso para tipos condicionais é verificar se um tipo é um array. O TypeScript fornece um tipo `Array` integrado, mas às vezes precisamos verificar se um valor é um array em tempo de execução. Podemos criar um tipo que verifica se um tipo é um array da seguinte forma:
```typescript
type IsArray<T> = T extends any[] ? true : false;
```

Aqui, `IsArray` recebe um parâmetro de tipo `T` e retorna um booleano indicando se `T` é um tipo de array. Por exemplo:
```typescript
type A = IsArray<number[]>; // true
type B = IsArray<string>; // false
```

Os tipos condicionais também suportam a palavra-chave `infer`, que nos permite inferir um tipo de outro tipo. Por exemplo, considere o seguinte tipo:
```typescript
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any;
```

Aqui, ReturnType recebe um tipo de função `T` e retorna o tipo de retorno de `T`. Por exemplo:
```typescript
function add(a: number, b: number): number {
  return a + b;
}

type Result = ReturnType<typeof add>; // number
```

Este tipo retorna `number`, pois esse é o tipo de retorno da função `add`.

Além disso, a página aborda como combinar tipos condicionais com outros recursos do TypeScript, como tipos genéricos e uniões de tipos. Por exemplo, podemos criar um tipo que verifica se um tipo é uma união de tipos de objeto da seguinte forma:
```typescript
type IsObjectUnion<T> = T extends object ? T[keyof T] : never;

type ExampleUnion = { a: string } | { b: number };

type Result = IsObjectUnion<ExampleUnion>; // string | number
```

Este tipo retorna `string | number`, pois `ExampleUnion` é uma união de tipos de objeto que contém `a: string` e `b: number`.

### Conclusão

Os tipos condicionais são uma ferramenta poderosa do TypeScript para tipos mais flexíveis e precisos. Com os tipos condicionais, é possível extrair propriedades de objetos, verificar se um tipo é um array e inferir tipos de outros tipos, além de combinar tipos condicionais com outros recursos do TypeScript para criar tipos mais complexos e úteis.

### Referências

Documentação oficial: https://www.typescriptlang.org/
Créditos da imagem: https://brandontle.com/writing/why-typescript/
Introdução prática: https://auth0.com/blog/working-with-typeScript-a-practical-introduction/