DEV Community

Fernando Daciuk
Fernando Daciuk

Posted on • Edited on

O que é o "is" no TypeScript?

O Junior Martins postou essa dúvida no nosso Discord, e eu respondi ele por lá, mas o Gabe sugeriu que a resposta virasse um post, então aqui está!


O is é usado quando você quer criar seus próprios type guards: você cria uma função que retorna um booleano (esse tipo de função se chama "predicado"), e usa o is para dizer que, se a função retornar true, quer dizer que o valor passado via argumento é de um tipo específico.

Por exemplo: imagine que você vai fazer um request para uma API, para buscar um produto.

A resposta pode ser um objeto com os dados do produto ou null, se o produto não for encontrado.

Poderíamos representar em tipos assim:

type Product = {
    id: string
    name: string
    price: number
}

type ProductResponse = Product | null
Enter fullscreen mode Exit fullscreen mode

Agora vamos fazer uma função que vai fazer esse request (fake, obviamente).. ignore a implementação, a ideia é só retornar um objeto do tipo ProductResponse, ou seja: ou vai retornar um Product, ou vai retornar null:

async function getProduct (id: string): Promise<ProductResponse> {
    const product = {
        id,
        name: 'TV',
        price: 5000
    }

    const possibleResponse = [product, null]
    const index = Math.floor(Math.random() * possibleResponse.length)
    return possibleResponse[index]
}
Enter fullscreen mode Exit fullscreen mode

Ao executar essa função, o retorno vai ser uma Promise de ProductResponse. Se você tentar acessar o valor de price da resposta - já que a função pode retornar um produto - o TS vai mostrar o erro:

Object is possibly 'null'
Enter fullscreen mode Exit fullscreen mode

A chamada seria algo assim:

getProduct('tv123').then(response => {
    console.log(response.price)
})
Enter fullscreen mode Exit fullscreen mode

Para garantir que o objeto não é nulo, precisamos usar um type guard, verificando o tipo do dado para garantir que ele não é nulo antes de acessar as propriedades do objeto:

getProduct('tv123').then(response => {
    if (response === null) {
        // aqui o TS sabe que `response` é null
        return
    }
    // aqui o TS sabe que `response` é `Product`, pois `null`
    // e `Product` são as únicas possíveis opções para `response`
    console.log(response.price)
})
Enter fullscreen mode Exit fullscreen mode

Usar um if com uma comparação por algum tipo (testando se o dado é igual a null, ou usando o typeof, por exemplo), é um type guard interno do TypeScript. Mas pense o seguinte: e se você quiser criar seu próprio type guard? Possivelmente você tentaria fazer algo assim:

function isProduct (value: ProductResponse) {
    return value !== null
}
Enter fullscreen mode Exit fullscreen mode

E então testaria o valor:

getProduct('tv123').then(response => {
    if (!isProduct(response)) {
        return
    }

    console.log(response.price)
})
Enter fullscreen mode Exit fullscreen mode

A surpresa acontece quando descobrimos que o TS agora não consegue inferir que response só será null se cair no if, e dá o mesmo erro:

Object is possibly 'null'.
Enter fullscreen mode Exit fullscreen mode

É aí que entra o is!

Como a função isProduct é apenas uma função que recebe um argumento qualquer, e retorna um valor qualquer, o TS não vai conseguir inferir que o retorno é um Product, e que esse retorno está diretamente relacionado com o argumento passado para a função.

Para fazer o TS entender essa relação, usamos o is: temos que dizer ao TS que, quando executarmos a função isProduct passando o argumento response, se a função retornar true, quer dizer que o argumento É, de fato, um Product.

Para dizer isso ao TS, só precisamos alterar o tipo de retorno da função isProduct:

function isProduct (value: ProductResponse): value is Product {
    return value !== null
}
Enter fullscreen mode Exit fullscreen mode

Pronto! Agora o TS vai saber quando o response é null e quando ele é um Product em um escopo completamente diferente da função isProduct!

getProduct('tv123').then(response => {
    if (!isProduct(response)) {
        // agora o TS sabe - novamente - que `response` é null, pois estamos
        // verificando se `response` NÃO é um `Product`
        return
    }
    // e aqui o TS sabe que `response` é `Product`, pois `null`
    // e `Product` são as únicas possíveis opções para `response`
    console.log(response.price)
})
Enter fullscreen mode Exit fullscreen mode

Podemos também criar um type guard mais genérico, para testar se o valor é null, fazendo uma verificação positiva, ao invés de criar um if negativo, que dificulta um pouco a leitura:

function isNull (value: unknown): value is null  {
  return value === null
}
Enter fullscreen mode Exit fullscreen mode

E então só precisamos usar essa função de type guard:

getProduct('tv123').then(response => {
    if (isNull(response)) {
        // aqui dentro o TS sabe que `response` é null
        return
    }
    // e aqui o TS sabe que `response` é `Product`, pois `null`
    // e `Product` são as únicas possíveis opções para `response`
    console.log(response.price)
})
Enter fullscreen mode Exit fullscreen mode

E tudo funciona como esperado, inclusive o autocomplete das propriedades price, name e id em response ;)

Você pode brincar com esse código direto do Playground do TypeScript!

Por hoje é só! Qualquer dúvida ou sugestão, deixe nos comentários :D


Sobre o autor

Você encontra minhas informações de contato e redes sociais rodando o seguinte comando no seu terminal:

npx fdaciuk
Enter fullscreen mode Exit fullscreen mode

Top comments (4)

Collapse
 
alexandre1202 profile image
Alexandre Barbosa

olha Fernando... essa foi a primeira vez que vi algo didático de Typescript e pra mim foi algo que ajudou a desvendar bastante coisa... porque o TS vem de encontro com linguagem tipada e facilita muito meu entendimento.

Obrigado por compartilhar e por favor, me adiciona se você tiver mais conteúdo de TS também para ficar na lista de leitura.

Forte abraço

Collapse
 
fdaciuk profile image
Fernando Daciuk

Massa Alexandre, que bom que curtiu! Eu pretendo começar a compartilhar mais conteúdo de TS escrito sim :D

Collapse
 
brenonovelli profile image
Breno Novelli

Li ontem e já me ajudou hoje. Valeu!

Collapse
 
fdaciuk profile image
Fernando Daciuk

Que massa @brenonovelli :D