DEV Community

Cover image for Como implementar Type guards no seu código
Samuel Monteiro
Samuel Monteiro

Posted on

2 1

Como implementar Type guards no seu código

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:

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;
}
Enter fullscreen mode Exit fullscreen mode

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

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...');
Enter fullscreen mode Exit fullscreen mode

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:

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

console.log(userResponse.data.email);
Enter fullscreen mode Exit fullscreen mode

Você vai tomar o seguinte erro:

Property 'email' does not exist on type 'User | Songs[]'.
  Property 'email' does not exist on type 'Songs[]'
Enter fullscreen mode Exit fullscreen mode

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:

const userResponse = fetch('https://apiSong...');
Enter fullscreen mode Exit fullscreen mode

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:

const isUser = (data: User | Song): data is User => (data as User).email !== undefined;
Enter fullscreen mode Exit fullscreen mode

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:

if(isUser(userResponse.data)) {
  console.log(userResponse.data.email)
};
Enter fullscreen mode Exit fullscreen mode

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:

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

if(isSong(songResponse.data)) {
  console.log(songResponse.data.title);
}
Enter fullscreen mode Exit fullscreen mode

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. 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 🤙

Sentry blog image

How I fixed 20 seconds of lag for every user in just 20 minutes.

Our AI agent was running 10-20 seconds slower than it should, impacting both our own developers and our early adopters. See how I used Sentry Profiling to fix it in record time.

Read more

Top comments (0)

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay