O que é o "is" no TypeScript?
Tags: typescript, javascript, ptbr
Published: 2022-02-08 18:41:47

O [Junior Martins](https://twitter.com/juniormartinxo) postou essa dúvida no nosso [Discord](https://discord.gg/x99eevqaHd), e eu respondi ele por lá, mas o [Gabe](https://www.linkedin.com/in/gabe-pinheiro/) sugeriu que a resposta virasse um post, então aqui está!

---

O `is` é usado quando você quer criar seus próprios _[type guards](https://www.typescriptlang.org/docs/handbook/2/narrowing.html#typeof-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:

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

type ProductResponse = Product | null
```

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`:

```ts
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]
}
```


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'
```

A chamada seria algo assim:

```ts
getProduct('tv123').then(response => {
    console.log(response.price)
})
```
 
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:

```ts
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)
})
```

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:

```ts
function isProduct (value: ProductResponse) {
    return value !== null
}
```

E então testaria o valor:

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

    console.log(response.price)
})
```

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'.
```

É 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`:

```ts
function isProduct (value: ProductResponse): value is Product {
    return value !== null
}
```


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

```ts
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)
})
```

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:

```ts
function isNull (value: unknown): value is null  {
  return value === null
}
```

E então só precisamos usar essa função de _type guard_:

```ts
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)
})
```

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!](https://www.typescriptlang.org/play?#code/C4TwDgpgBACgTgewCYFcDGwoF4oG8BQURUAlkgFxQDOwcJAdgOaHH0CGAthJTXUy0TB003KPRQcARhDj4AvvnyhIsRKgwAlCFTAJ6VaDnjJ0mAD5iUAGyuK2VEPTRQAZiifASeqIwjBj6pgAFGQ8tAyMAJSUxhwkBgA8AaZaOnoGAHx4AlBo6ZhCJhjY2cRlpEgANDll7FyUAOQAKgBqDdXlxEIkIpQArAAMQzkKOXn6BQhUVCSSVhCpuvqGUADahYGVljYAumP5pPRIEAAeJQCybMAAFgB0LlYICHBBlze3cGxHCBxBkVAAKigumms3mi3SEFu8yYN0iOTgfhQcHowKmMzmC20SwMqwYxxOe1Gbg8XlR8WSxSCADc2FYUKJKcAIctolBafToPFVEVMAQyojgMjURyGVAAIRYHDiGzyRQkjBk0hUABy1isUBpdIZlHcAGt6AgAO70NmirlUbYa-nEQXC9nawxSq1y-C+fxqUxBBrAakARgATABmBqRW43CD0IKItLLbBZG1EEguTXxNU2aPYyGRf6J8oAenzUDYAEcUCQoMd6LQEFBa00AMrUNjSKBl6AAAxjOIgHagAEuXZ0iHaUSMcoWoNBS+W61BG83W+2oF2s8s+4OO0yO1tdNyOzKrB2J0XO9vqABj2v2KAAL-oPRvIKoAFvqRBuQgwABzgCv2mBNhPhXbtIWPMpxioBB5mhBBGEzWMDFuboRHhORIiAA)

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
```