O que há de novo no TypeScript 4.4
Tags: typescript, javascript, development
Published: 2021-09-30 21:12:52

---
title: O que há de novo no TypeScript 4.4
published: true
date: 2021-09-15 11:00:00 UTC
tags: typescript,javascript,development
canonical_url: https://blog.lsantos.dev/typescript-44/
cover_image: https://raw.githubusercontent.com/khaosdoctor/blog-assets/master/images/2021/09/TypeScriptImage-scaled.jpg
---

No dia 26 de Agosto de 2021, tivemos o [anúncio](https://dev.to/danielrosenwasser/announcing-typescript-4-4-hbn-temp-slug-2194952) da versão 4.4 do **TypeScript** e, como é de costume, vou fazer um highlight de tudo que aconteceu de novo e todas as novidades mais legais do nosso superset favorito!

## Análise de fluxo agora com variáveis

Quando usamos TypeScript, uma das grandes falácias que muita gente descreve como sendo um problema que impede o uso, é ter que ficar declarando tipos para todos os dados que você tem. Isso não é verdade.

O compilador do TS é poderoso o suficiente para entender o fluxo de controle e o fluxo do seu código, de forma que ele sabe quando uma variável ou algum outro dado é de um tipo específico de acordo com uma checagem feita anteriormente. Essa checagem é comumente chamada de _type guard_. E é quando fazemos algo assim:

```
function foo (bar: unknown) {
  if (typeof bar === 'string') {
    // O TS agora sabe que o tipo é String
    console.log(bar.toUpperCase())
  }
}
```

Isso é válido não só para casos de `unknown` mas também para casos onde o tipo é genérico como `any`.

O grande problema é que, se movermos essa verificação para uma constante ou uma função, o TS se perde no fluxo e não consegue mais entender o que está acontecendo, por exemplo:

```
function foo (bar: unknown) {
	const isString = typeof bar === 'string'
    if (isString) console.log(arg.toUpperCase())
    // ~~~~~~~~~~~
    // Error! Property 'toUpperCase' does not exist on type 'unknown'.
}
```

Agora, o TS consegue identificar a constante e o seu retorno, conseguindo prover o resultado sem erros. O mesmo é possível também em tipos complexos, ou tipos discriminantes (_discriminant types_):

```
type Animal = 
    | { kind: 'cat', meow: () => void }
    | { kind: 'dog', woof: () => void }

function speak (animal: Animal) {
  const { kind } = animal
  
  if (kind === 'cat') { animal.meow() }
  else { animal.woof() }
}
```

Dentro dos tipos extraídos pelo _destructuring_, agora temos a asserção correta da string. Outra coisa legal é que ele também vai entender de forma transitiva como todos os tipos funcionam, ou seja, ele vai tipo a tipo para poder inferir qual é o tipo atual do objeto a partir das análises que você já fez:

```
function f(x: string | number | boolean) {
    const isString = typeof x === "string"
    const isNumber = typeof x === "number"
    const isStringOrNumber = isString || isNumber
    if (isStringOrNumber) {
        x // Type of 'x' is 'string | number'.
    }
    else {
        x // Type of 'x' is 'boolean'.
    }
}
```

## Index signatures com Symbols e templates

Existe um tipo chamado de _index signature_, essencialmente este tipo nos diz que o objeto em questão pode ter chaves de nome arbitrário, como se fosse um dicionário eles são representados como `[key: string]: any`.

Os únicos tipos possíveis para uma _index signature_ são _string_ e _number_ atualmente, porque são os tipos mais comuns.

Porém, existe um outro tipo chamado [Symbol](https://medium.com/trainingcenter/javascript-symbols-decifrando-o-mist%C3%A9rio-383e359e64e3), que é muito utilizado, principalmente por quem constrói libs, para poder indexar os tipos de seus arrays e objetos sem ter de exibí-los ou modificá-los. Com a chegada do 4.4 você agora pode fazer isso:

```
interface Colors {
    [sym: symbol]: number;
}

const red = Symbol("red");
const green = Symbol("green");
const blue = Symbol("blue");

let colors: Colors = {};

colors[red] = 255;    
let redVal = colors[red];  
```

Era impossível também ter um subset de _string_ ou de _number_ como os _template string types_ como sendo chaves. Por exemplo, um objeto cujas chaves começam sempre com `data-`, agora isso é totalmente válido:

```
interface DataOptions {
  [key: `data-${string}`]: unknown
}

let b: DataOptions = {
    "data-foo": true
    "qualquer-coisa": true, // Error! 'unknown-property' wasn't declared in 'DataOptions'.
};
```

## Catch agora tem padrão de `unknown`

Como muitas pessoas sabem (e reclamaram!), quando usamos um `try/catch` dentro de qualquer função no TypeScript, o bloco `catch` vai sempre levar um parâmetro `error` que, por definição, teria um tipo `any`.

Depois de algumas discussões com a comunidade sobre qual seria o tipo correto, muitas pessoas optaram por ter o tipo `unknown` como padrão para os erros. Isto porque deixar um tipo aberto como `any`, essencialmente não dá tipagem nenhuma. Então o TS 4.4 introduz uma nova opção no `tsconfig` e uma nova flag chamada `useUnknownInCatchVariables`, que está desativado por padrão para não quebrar a compatibilidade, mas pode e deve ser ativada.

```
try {
    codigo();
}
catch (err) { // err: unknown

    // Error! Property 'message' does not exist on type 'unknown'.
    console.error(err.message);

    // Define o tipo de erro
    if (err instanceof Error) {
        console.error(err.message);
    }
}
```

Se você ativar a flag `strict`, esta flag também será ativada.

## Propriedades opcionais exatas

Outro problema trazido pela comunidade era o conflito entre propriedades opcionais declaradas como `prop?: <tipo>`, pois este tipo de propriedade vai ser expandida para `prop: <tipo> | undefined`, mas e se a propriedade puder ter mesmo um valor `undefined`?

Então se alguém quisesse escrever uma propriedade opcional do tipo `number`, como `undefined`, isso era ok por padrão, mas causava vários problemas:

```
interface Pessoa {
  nome: string
  idade?: number
}
  
const Lucas: Pessoa = { nome: 'Lucas', idade: undefined } // ok
```

E esta prática ocorre em vários erros porque vamos estar tratando um valor válido com um inexistente. Ainda mais se tivéssemos que lidar com a propriedade `idade` em algum momento, além disso, cada tipo de método como o `Object.assign`, `Object.keys`, `for-in`, `for-of`, `JSON.stringify` e etc, tem tratamentos diferentes para quando uma propriedade existe ou não.

Na versão 4.4 o TS adiciona uma nova flag chamada `exactOptionalPropertyTypes`, que faz com que este erro desapareça, uma vez que você não poderá usar `undefined` em uma propriedade tipada como opcional.

```
interface Pessoa {
  nome: string
  idade?: number
}
  
const Lucas: Pessoa = { nome: 'Lucas', idade: undefined } // Erro
```

Assim como a anterior, a propriedade é parte do conjunto `strict`.

## Suporte a blocos estáticos

O ECMA2022 [prevê uma nova funcionalidade](https://github.com/tc39/proposal-class-static-block#ecmascript-class-static-initialization-blocks) chamada de _static initialization blocks_, essa funcionalidade vai permitir que criemos códigos mais complexos de inicialização para membros estáticos de uma classe, vamos falar mais sobre isso aqui no blog em breve!

Mas por enquanto, o TS 4.4 já tem suporte a essa funcionalidade.

## Conclusão

Estas foram as alterações mais importantes no TS 4.4, mas não as únicas, tivemos uma série de melhorias de performance e também de leitura e integração com o VSCode.