Home >Blog >

TypeScript: O guia definitivo

11/10/2023

avatar
Post image

TypeScript: O guia definitivo

Um guia completo para sua jornada TypeScript

Este artigo descreve os recursos e funcionalidades até o TypeScript 3.1.

Uma das linguagens mais interessantes para o desenvolvimento de aplicativoss em larga escala é o TypeScript, da Microsoft . O TypeScript é único porque é um superconjunto de JavaScript (ES2015+), mas com tipos, interfaces, tipos genéricos e muito mais. Ao contrário de outras linguagens que compilam para JavaScript, o TypeScript não tenta alterar o JavaScript para uma nova linguagem. Ao invés disso, a equipe do TypeScript toma um cuidado extra para alinhar os recursos de uma maneira bem próxima ao JavaScript e suas funcionalidades futuras. Devido a isso, os desenvolvedores que utilizam TypeScript podem aproveitar os recursos mais recentes da linguagem JavaScript, além de um sistema de tipagem forte para escrever códigos mais organizados, aproveitando as ferramentas avançadas que o uso de uma linguagem de tipo estaticamente fornece.

O poder real no TypeScript vem de suas ferramentas. Os tipos são um meio de levar ferramentas de classe mundial à linguagem JavaScript, o que permite projetos melhores estruturados e mais fáceis de manter. Isso é especialmente importante à medida que os projetos JavaScript aumentam de tamanho (seja em linhas de código ou desenvolvedores no projeto). Ter uma conclusão rápida e precisa, recursos de refatoração e feedback imediato faz do TypeScript a linguagem ideal para JavaScript em grande escala. O TypeScript também simplifica seu uso para quem quer iniciar com a linguagem! Como o JavaScript é efetivamente o TypeScript sem anotações de tipo, todas ou partes de um projeto existente podem ser convertidas imediatamente e, em seguida, podemos aproveitar lentamente tudo o que o TypeScript tem a oferecer ao longo do tempo.

Embora a documentação do TypeScript tenha melhorado significativamente desde que este guia foi publicado, este guia ainda fornece uma das melhores visões gerais dos principais recursos do TypeScript, supondo que você já tenha um conhecimento razoável de JavaScript e um entendimento básico de como funciona a herança baseada em classe (como em Java, PHP, C #, etc.). O guia é atualizado regularmente para fornecer novas informações sobre as versões mais recentes do TypeScript.

Índice

  • Instalação e uso

  • Configuração

  • Utilizando let e const

  • Importação e exportação

  • Açucares de linguagem

  • Desestruturação

  • Outros recursos da linguagem

  • Tipos

  • Tipos de Objeto

  • Tipo “object” em TS 2.2+

  • Tipos de Tupla

  • Tipos de Função

  • Funções sobrecarregadas

  • Tipos rigorosos para Funções em TS 2.6+

  • Tipos Genéricos

  • Tipos de União

  • Tipos de Intersecção

  • Tipando o valor de “this”

  • Tipos Mapeados em TS 2.1+

  • Tipos “Mapped”, “Partial”, “Readonly”, “Record” e “Pick” em TS 2.1+

  • Tipos Condicionais em TS 2.8+

  • Guardas de Tipo

  • Classes

  • Mixins e Herança múltipla

  • Enumeráveis

  • Aliases

  • Declarações de Ambiente

  • Carregador de Plugins

  • Análise de controle de fluxo

Instalação e uso

A instalação do TypeScript é tão simples quanto a execução npm install typescript. Uma vez instalado, o compilador TypeScript estará disponível executando tsc ou executando uma tarefa local para compilar automaticamente após cada arquivo ser salvo. Se você quiser experimentar o TypeScript em seu navegador, o TypeScript Playground permite que você experimente o TypeScript com um editor de código completo, com a limitação de que os módulos não podem ser usados. A maioria dos exemplos deste guia pode ser colado diretamente no playground para ver rapidamente como o TypeScript é compilado em JavaScript com uma fácil leitura.

Configuração

O compilador TypeScript é altamente configurável, permitindo que o usuário defina onde os arquivos de origem estão e como eles devem ser transformados, indo até como podemos configurar quão restrito o verificador de tipos deve ser e se deve permitir arquivos JavaScript. Cada uma das opções de configuração pode ser passada para o comando tsc, ou um arquivo tsconfig.json pode armazenar as configurações de um projeto, definindo como o compilador deve ser executado toda vez. O tsconfig.json armazena informações sobre vários sinalizadores e configurações do compilador, bem como informações de resolução do caminho do módulo. Uma lista completa de opções do compilador está disponível na documentação oficial. Um exemplo de tsconfig.json do projeto Dojo:

{
  "version": "2.1.5",
  "compilerOptions": {
    "declaration": false,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "module": "umd",
    "moduleResolution": "node",
    "outDir": "_build/",
    "removeComments": false,
    "sourceMap": true,
    "strict": true,
    "target": "es5",
    "lib": ["es2015", "dom"]
  },
  "include": [
    "./src/**/*.ts", 
    "./tests/**/*.ts", 
    "./typings/index.d.ts"
  ]
}

O principal benefício de incluir um tsconfig.json em um projeto é garantir que cada execução do compilador seja idêntica comparação à execução anterior e que todos os contribuidores do projeto estejam executando com os mesmos sinalizadores. O compilador também tem vários sinalizadores que alteram a rigidez do compilador quando se trata de verificação de tipos e se devem ser permitidos arquivos JavaScript. Essa é uma das melhores partes do TypeScript, pois permite que o TypeScript seja adicionado a um projeto existente sem exigir que o projeto inteiro seja convertido para o TypeScript e totalmente tipado primeiro. Bandeiras como noImplicitAny, quando false, não emitirá um aviso de compilador sobre uma variável que não é anotada com um tipo ou onde um tipo não pode ser inferido. Com o tempo, um projeto pode habilitar esse e outros sinalizadores, permitindo que uma equipe trabalhe, incrementalmente, em direção ao código totalmente tipado. Para novos projetos iniciados no TypeScript, recomenda-se que o sinalizador strict seja habilitado para receber todos os benefícios do TypeScript.

A propriedade lib no tsconfig.json pode ser usada para especificar quais bibliotecas padrão estão disponíveis para o projeto. Os valores aqui são baseados no tipo de projeto e onde o projeto precisa ser executado. Por exemplo, um projeto para Web provavelmente precisará acessar o DOM, portanto, a inclusão de lib: [“dom”] garantirá a existência de document e métodos como querySelector. Se estiver rodando em um ambiente ES2016, ou um com os polyfills apropriados, “es2016” também pode ser incluído. Se o seu projeto especificamente espera que os arrays tenham um método .include, podemos incluir “es2016.array.include”. Isso permite a personalização de como o TypeScript pode identificar o seu código, comparando com o ambiente desejado em tempo de execução e se deve lançar erros para o código atual, que pode não existir realmente no ambiente em tempo de execução.

A partir do TypeScript 2.7, existe uma nova diretiva de referência para lib, isolando a inclusão de uma biblioteca específica apenas no arquivo que usa essa diretiva. Isso é benéfico para isolar recursos novos/experimentais em um único arquivo e não disponibilizá-los para o projeto inteiro. Considere o seguinte exemplo:

/// <reference lib="es2016.array.include" />
 
[ 'foo', 'bar', 'baz' ].includes('bar'); // true

O compilador não lançará um erro sobre o uso Array.prototype.includes neste módulo, porque ele contém a diretiva lib. No entanto, se outro arquivo no projeto tentar usar esse método e a lib não for incluída no tsconfig.json, o compilador lançará um erro.

Muitos projetos também incluem um tslint.json para especificar configurações de linter e um arquivo package.json que é padrão na maioria dos pacotes JavaScript.

O suporte a Glob foi adicionado para o TypeScript 2.0, facilitando o include e exclude em grupo de arquivos seguindo padrões que aproveitam *, ?, **/.

Observe que, a partir do TypeScript 2.1, o tsconfig.json pode herdar de outros arquivos de configuração, reduzindo a duplicação em aplicativos e bibliotecas complexos. Isto é feito através da chave extends que tem um caminho como um valor.

Utilizando let e const

O ES2015 introduz duas novas maneiras de declarar variáveis ​​usando let e const. É preferível declarar variável usando let ou const ao invés de var porque as declarações var têm algumas regras de escopo incomuns em comparação com outras linguagens. Ao contrário das variáveis ​​declaradas com var, aquelas declaradas com let são variáveis ​​de “escopo de bloco”, que não são visíveis fora de seu bloco mais próximo ou loop. Isso ajuda a evitar colisões não intencionais na reutilização de nomes de variáveis. As variáveis ​​declaradas usando const são de escopo similar àquelas declaradas com let — a grande diferença é que é um erro é retornado em tempo de compilação se você tentar reatribuir o valor de uma const. No entanto, você ainda pode alterar as propriedades dentro de um objeto mantido por uma variável const. Utilizar const quando possível ajuda um programador a sinalizar sua intenção sobre como essas variáveis ​​se comportarão — o que torna o código mais fácil de entender.

O TypeScript 2.0 adicionou a palavra chave readonly — que não permite a reatribuição e implica uma propriedade não gravável/uma propriedade acessível apenas via get. Isso não significa que os não-primitivos sejam imutáveis.

Importação e exportação

Para começar a falar sobre como escrever o TypeScript, primeiro precisamos entender como criar e carregar arquivos do TypeScript. Os arquivos TypeScript usam a extensão de arquivo .ts e, como o AMD e o CommonJS , cada arquivo TypeScript representa nominalmente um único módulo. A importação de módulos no TypeScript segue a API ES Modules (ESM):

import myModule from './myModule';

Por padrão, a resolução do caminho de módulos para IDs de módulo relativo é a mesma no TypeScript que no AMD e no CommonJS, em que a ID do módulo é resolvida em relação ao diretório que contém o módulo de referência. IDs de módulo absoluto funcionam de forma ligeiramente diferente; primeiro, se houver uma declaração de módulo de ambiente correspondente, seu valor será usado. Caso contrário, o compilador percorre o sistema de arquivos, a partir do diretório que contém o módulo de referência, procurando .ts, então .d.ts, em cada diretório pai, até encontrar uma correspondência.

Antes do TypeScript 2.0, existia um suporte para duas maneiras de resolver nomes de módulos: classic (um nome de módulo sempre é resolvido para um arquivo, os módulos são pesquisados ​​usando uma pasta walk) e node (usa regras similares ao carregador de módulo Node.js). Infelizmente, nenhuma abordagem resolve a abordagem de definir módulos relativos a um baseUrl, que é o que os sistemas AMD, como Dojo e RequireJS e SystemJS usam, embora os esforços estejam em andamento na proposta de padrões de URLs nomeados/mapeados.

Ao invés de introduzir um terceiro tipo de resolução de módulos para o TypesSript 2.0, a equipe resolveu adicionar definições de configuração para resolver este problema: baseUrl, paths, e rootDirs.

paths só pode ser usado se baseUrl estiver definido. Se pelo menos uma dessas propriedades for definida, o compilador do TypeScript tentará usá-las para resolver os nomes dos módulos e, se falhar, recorrerá a uma estratégia padrão.

Para exportar valores de um módulo, podemos utilizar a palavra-chave export:

export function foo() {}
export let bar = 123;
export class MyClass {}

Importação de todo um módulo, pode ser feita usando *asterisco ()**, isso fará com que as exportações do módulo fiquem disponíveis localmente com os mesmos nomes: foo, bar, e MyClass. Para usar esses valores em outro módulo, basta utilizar import para acessar o módulo e suas propriedades exportadas:

import * from './myModule';

foo();
bar + 5; // = 128
new MyClass();

Para importar propriedades individuais, coloque os nomes das propriedades com chaves:

import { foo } from './myModule';
import { bar, MyClass } from './myModule';

foo();
bar + 5; // = 128
new MyClass();

Você pode especificar uma exportação padrão adicionando a palavra-chave default imediatamente após export:

export default class MyClass {}

Isso é equivalente a retornar um valor de uma função de fábrica (factory function) no AMD ou atribuir um valor ao module.exports em CommonJS. Para utilizar o módulo, você pode simplesmente importá-lo diretamente:

import MyClass from './MyClass';

let myInstance = new MyClass();

Fornecer um identificador de importação sem chaves irá carregar a importação padrão e será implicitamente nomeada ao que você especificar. Outras importações podem ser nomeadas usando a palavra-chave as:

// AnotherClass = MyClass from MyClass.ts
import AnotherClass from './MyClass';

import { foo, bar as baz } from './myModule';

Para anexar as exportações de um módulo a uma propriedade nomeada, como quando você atribui propriedades ao objeto exports em AMD ou CommonJS, podemos fornecer um alias para a importação com asterisco:

import * as foo from './myModule';

foo.foo();
foo.bar;
new foo.MyClass();

Observe que ao usar mixins com classes TypeScript (2.2+), há alguns detalhes sutís descritos na seção sobre classes.

Açucares de linguagem

Antes de mergulhar nos recursos de tipagem estática do TypeScript, é essencial revisar algumas das melhorias gerais feitas nas funções do TypeScript, já que algumas dessas alterações tornam os recursos do sistema de tipos mais fáceis de entender.

O TypeScript inclui quatro melhorias principais em funções: parâmetros opcionais, valores de argumentos padrão, associar parâmetros restantes e funções de seta.

Parâmetros opcionais agora podem ser definidos pela sufixação de um identificador de parâmetro com um ponto de interrogação:

function getRange(max, min, exclusive?) {
  // ...
}

Aqui exclusive é um parâmetro opcional. Isso não tem sentido quando se fala em JavaScript, já que todos os parâmetros são sempre opcionais, mas no TypeScript, o compilador proíbe omitir os argumentos tipados, a menos que eles sejam especificados como opcionais ou tenham um valor padrão.

Um parâmetro opcional é essencialmente uma forma abreviada de especificar undefined como o valor padrão para um parâmetro. Tornar um parâmetro opcional com um valor diferente é tão simples quanto atribuí-lo à lista de parâmetros, ao invés de usar a abreviação de ponto de interrogação, fazemos:

function getRange(max, min = 0, exclusive = false) {
  // ...
}

Nesse caso, min é opcional e terá como padrão 0, e exclusive é opcional e será padronizado com false.

O TypeScript também adiciona suporte para um parâmetro variádico no final com …rest, que coleta quaisquer argumentos extras passados ​​para a função em um único array nomeado:

function publish(topic, ...args): void {
  // ...
}

Neste caso, ao chamar publish(‘/foo’, ‘a’, ‘b’, ‘c’), topic uma string ‘/foo’ e args seria um array [‘a’, ‘b’, ‘c’]. Observe que o uso desse recurso adiciona um loop extra à sua função que é executada em cada chamada para coletar argumentos restante no parâmetro nomeado, portanto, se você tem um código crítico, para melhorar seu desempenho você deve continuar utilizando diretamente o objeto arguments.

O TypeScript também inclui suporte para a função de seta do ES2015. Esse novo tipo de função fornece uma nova sintaxe abreviada e também altera a maneira como a palavra-chave this funciona, portanto, seu valor é obtido do escopo léxico mais próximo, ao invés do objeto de contexto no qual está sendo chamada, como as funções normais do JavaScript:

let obj = {
  arrow: () => {
    console.log(this);
  },
  regular: function () {
    console.log(this);
  }
};
 
obj.arrow(); // logs `window`
obj.regular(); // logs `obj`

O TypeScript também inclui notações abreviadas de objetos que reduzem a quantidade de código necessária para operações comuns com associar objetos literais:

const foo = 'foo';
let a = {
  // notação abreviada de identificador, igual à `foo: foo`
  foo,
 
  // notação abreviada para métodos, igual à `bar: function () {}`
  bar() {
 
  }
};

Desestruturação

Diversas variáveis ​​podem ser atribuídas diretamente de um array:

let x, y;
[x, y] = [10, 20];

Podemos encurtar:

let [x, y] = [10, 20];

Desestruturação também funciona com objetos:

let { place, location: [x, y] } = { place: 'test', location: [10, 20] };
// variável local 'place' = 'test'
// variável local 'x' = 10
// variável local 'y' = 20

Outros recursos da linguagem

O TypeScript também inclui vários outros recursos das especificações atuais ou futuras do ECMAScript, incluindo: