Como implementar Type guards no seu código
Tags: typescript, javascript
Published: 2021-05-20 00:21:19

Imagine que você tem a seguinte situação:

> Você tem uma aplicação que busca por músicas no Spotify e nessa aplicação você mostra o `email` do usuário logado e a música que ele está escutando naquele momento.

Você então cria 2 funções para acessar a API do Spotify

- `getUser` para chamar a API que irá buscar os dados do usuário
- `getSong` que irá retornar uma lista de músicas do Spotify

Então você teria o seguinte código:

```tsx
type APIResponse<T> = {
  data: T;
  error: Error;
}

type User = {
  email: string;
}

type Song = { id: string; title: string };

const getUser = async (fetchUrl: string): APIResponse<User> => {
    const res = await fetch(fetchUrl);
    
    return res;
}

const getSong = async (fetchUrl: string): APIResponse<Song> => {
    const res = await fetch(fetchUrl);
    
    return res;
}
```

Depois de implementar as 2 funções, você percebe que elas são muito parecidas. As 2 recebem uma `url` que é passada para dentro do método `fetch`. Esse método faz a chamada para a `API` e depois retorna o resultado.

E aí, você pode pensar
> poxa, já que elas são tão parecidas, acho que vou criar uma função que reduz a quantidade de código repetido

```tsx
const fetchApi = async (fetchUrl: string): APIResponse<User | Song> => {
	const res = await fetch(fetchUrl);
    
  return res;
}

const userResppnse = await fetchApi('https://apiUser...');
const songResponse = await fetchApi('https://apiSong...');
```

Já parece que ficou melhor. Agora temos menos código duplicado e menor possibilidade de erro.

Mas você vai ter um problema de `Typescript` 😟

O tipo da variável `userResponse` está como `APIResponse<User | Song>` então se você tentar fazer isso:

```tsx
const userResponse = await fetchApi('https://...');

console.log(userResponse.data.email);
```

Você vai tomar o seguinte erro:

```tsx
Property 'email' does not exist on type 'User | Songs[]'.
  Property 'email' does not exist on type 'Songs[]'
```

O Typescript não consegue dizer se a propriedade `data` da variável `userResponse` é um `User` ou um `Song` e por isso ele previne que você chame a propriedade `email` que é exclusiva do `User`.

Isso garante que se por exemplo isso acontecer:

```tsx
const userResponse = fetch('https://apiSong...');
```

Você não vai ter problemas mais pra frente.

### Type guards
Os `type guards` são uma forma de dizer para o `Typescript` qual tipo nós esperamos.

Podemos criar um `type guard` de usuário que garante o tipo `User` da seguinte forma:

```tsx
const isUser = (data: User | Song): data is User => (data as User).email !== undefined;
```

Estamos criando a função `isUser` que aceita o tipo `data: User | Song` e que retorna um `data is User`. A expressão `is User` diz que eu, como desenvolvedor, garanto que o retorno da minha função é do tipo `User`.

O que falta é a implementação dessa verificação. Já que o tipo `Song` não tem o campo `email` podemos verificar se a variável `data` possuí ele. 

Se possuir, podemos dizer que `data` é do tipo `User`.

E com isso, podemos executar o seguinte código sem problemas de `Typescript`:

```tsx
if(isUser(userResponse.data)) {
  console.log(userResponse.data.email)
};
```

Assim, o `Typescript` não vai mostrar um erro porque você disse que a função `isUser` retorna `User` e nada mais.

Não tem possibilidade da variável `data` ser do tipo `Song` por exemplo.

E para finalizar, o type guard do tipo `Song` seria assim:

```tsx
const isSong = (data: User | Song): data is Song => (data as Song).title !== undefined;

if(isSong(songResponse.data)) {
  console.log(songResponse.data.title);
}
```

---

Através dos `type guards` você consegue ter maior controle sobre a tipagem do seu código. Tudo isso em tempo de `runtime`, o que é bem legal 🤘

> Apesar de ser uma boa adição na sua base de código, o `type guards` é propenso a falhas já que quem define a função para validar o tipo é o próprio desenvolvedor. 

> Para resolver essas falhas e realmente trabalhar com um `typchecking` em `runtime`, sugiro dar uma lida [nesse artigo do pessoal do Unsplash](https://unsplash.com/blog/user-defined-type-guards-not-safe/). O artigo destaca o porquê de `type guards` definidos pelo usuário não serem tão seguros e o que fazer nesses casos.

---

Galera, é isso por hoje. Fiz esse artigo porque acabei me deparando com algumas situações na minha aplicação que funcionariam melhor se tivesse um `type guard` implementado. E apesar de serem muito úteis, não vejo muita gente usando no dia a dia, então fica aqui minha contribuição. Espero que consigam aproveitar de alguma forma 😉

E se vocês sentiram falta de alguma explicação, ficaram com alguma dúvida ou só querem trocar uma ideia, podem me marcar ou mandar DM no [twitter](https://twitter.com/samuelmpinho) 🤙