A novidades do TS 5.3

Como é de costume, eu quero trazer as principais novidades do TS a medida que elas vão saindo! Recentemente recebi a notícia de que o time do TypeScript está trabalhando em uma nova versão da linguagem, para isso, eles criam o chamado iteration plan.

O iteration plan não é um documento que mostra exatamente o que vai sair na linguagem, mas sim o que possivelmente pode estar nas próximas versões. Basicamente é um documento de intenções do time em aplicar e aprimorar o compilador.

A versão beta do TS 5.3 deve sair em setembro (um pouco antes do lançamento do primeiro módulo da Formação TS) e a versão final deve estar no ar em novembro!

O Matt do Total TypeScript compilou um pouco do que tem no documento nesse outro artigo, e eu vou aproveitar para poder resumir e explicar também fazendo um link com a documentação do JavaScript.

Import Attributes

Essa é uma proposta aberta há um tempo, e o objetivo principal é que você pode especificar algumas opções de validação dos imports de módulos. No momento só estamos falando da validação do tipo do módulo, por exemplo, pra validar que um módulo é um arquivo JSON:

import json from './foo.json' with { type: 'json' };

Isso é bem útil quando temos a questão de segurança, por exemplo, se um arquivo é esperado para ser JSON mas, na verdade é JS.

Você pode usar esses atributos em imports dinâmicos:

import("foo.json", { with: { type: "json" } });

Ou até mesmo exportar um módulo com outro tipo validado:

export { val } from './foo.js' with { type: "javascript" };

Isso também vale para WebAssembly ou workers diferentes:

new Worker("foo.wasm", {
  type: "module",
  with: { type: "webassembly" },
});

Usar o with seguido de type é uma forma de deixar essa funcionalidade bem aberta porque será possível incluir mais propriedades depois. Imagina ser possível importar um pacote por uma das propriedades do package.json ou então até mesmo do arquivo.

Throw expressions

Essa é uma funcionalidade que eu vou falar em um futuro vídeo, mas é algo que estava faltando no JS há muito tempo. A proposta para isso está ainda no estágio 2, o que é estranho para o TS, que costuma implementar a maioria das funcionalidades quando elas estão no estágio 3.

O que é possível acontecer é que, como eles mencionam no plano, o time vai estar "championing" a proposta, que é uma forma de dizer que eles vão estar ativamente trabalhando nela e empurrando para frente, para fazer chegar mais rápido ao estágio 3 e 4.

A ideia é que você possa usar throw fora de algum statement específico, por exemplo, em uma declaração de variável:

const userName = user.name || throw new Error('Name is required')

Hoje isso não é possível.

Isolated Declarations

Existe um problema quando se trabalha com monorepos em quase todas as linguagens, porém em TypeScript isso é pior. Porque quando temos pacotes que dependem de outros pacotes, isso acaba invariavelmente gerando uma quantidade absurda de complexidade, principalmente para o TypeScript.

Se você tem 10 níveis de pacotes que dependem entre si, o TS precisa inferir todos os tipos de todos os pacotes ele mesmo, começando de baixo para cima e ir gerando os arquivos declarativos de cada pacote para importar no pacote de cima. E isso é muito lento.

E como não existe uma forma mais rápida de fazer isso sem mudar no compilador, porque outras ferramentas como o esbuild ou até mesmo o swc não são inteligentes o suficientes pra isso, sobra para o TS inferir tudo, e ele não é necessariamente o compilador mais exigente de todos.

Essa proposta adiciona uma nova configuração no TSConfig chamada isolatedDeclarations.

{
  "compilerOptions": {
    "isolatedDeclarations": true
  }
}

O que ela faz é ativar um modo mais estrito, onde é necessário, por exemplo, colocar anotações de tipo em retorno de funções, principalmente para funções exportadas para fazer o TS não precisar inferir tudo.

Narrow de generics em funções

Existe um problema latente no TypeScript que é o fato de que ele não vai fazer um type narrowing em um tipo generico dentro de uma função quando a gente quer retornar algo baseado nesse tipo.

Complicado? Vamos lá. Imagina isso aqui:

interface F {
  "t": number,
  "f": boolean,
}

function depLikeFun<T extends "t" | "f">(str: T): F[T] {
  if (str === "t") {
    return 1;
  } else {
    return true;
  }
}

depLikeFun("t"); // number
depLikeFun("f"); // boolean

É exatamente isso que estamos falando, o que a gente quer é que, se a gente passar t, o tipo de retorno é number, se for f é boolean. Para alcançar esse resultado temos que ter um tipo T que é passado como um genérico da função, e depois retornamos a chave da interface F baseada em T.

Só que se fizermos isso atualmente, vamos ter um erro dizendo que nossos retornos não são do tipo never. Isto porque o TS não faz um narrowing do nosso tipo genérico T. Na verdade o que ele está fazendo, segundo essa issue, é que quando passamos F[T] estamos dizendo "escreva F na chave T", portanto ele vai checar se F[T] tem um tipo que intersecta ambas as suas possibilidades (no caso number e boolean), então number & boolean é never.

Autocomplete em tipos de string

Um hack interessante – que eu descobri recentemente – é que você pode criar um union type de strings e string & {}, dessa forma você ganha um auto complete do compilador, além de poder colocar qualquer outra string no lugar também:

type IconSize =
  | "small"
  | "medium"
  | "large"
  | (string & {});

Então isso seria totalmente válido:

const icons: IconSize[] = [
  "small",
  "medium",
  "large",
  "extra-large",
];

Na versão 5.3 pode ser possível tirar o & {} e usar apenas string para obter o mesmo resultado:

type IconSize =
  | "small"
  | "medium"
  | "large"
  | string;

O que vem por ai?

Esse é um artigo base, não sabemos se essas funcionalidades serão, de fato, adicionadas ao TS ou não, até porque o documento é um planejamento de trabalho e não um guia do que vai ser incluído por lá.

Espero que algumas dessas funcionalidades, especialmente os unions types e o type narrowing, sejam adicionadas porque vai quebrar um bom galho para a maioria das pessoas.

Além disso, estarei postando as novidades, com exemplos, lá na comunidade da Formação TS primeiro assim que o TS sair! Então chega mais!