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