TypeScript, tudo que você precisa saber sobre o superconjunto mais famoso de JavaScript

O TypeScript é uma linguagem de programação que se integra facilmente com diversos frameworks e bibliotecas JavaScript, como React, Angular, Vue e até mesmo com o Node.js no backend.

Com o TypeScript, os desenvolvedores podem definir tipos de dados para variáveis, parâmetros de função e propriedades de objeto. Isso ajuda a detectar erros em tempo de compilação e torna o código mais seguro e fácil de manter.

Além disso, é amplamente suportado por várias ferramentas de desenvolvimento populares, como o Visual Studio Code e o WebStorm, tornando a experiência do desenvolvedor ainda mais produtiva e eficiente.

Conheça os Cursos da Cubos Academy:

O que é o TypeScript?

É uma linguagem de programação de código aberto que foi desenvolvida pela Microsoft. Ela é uma extensão do JavaScript que adiciona recursos de linguagem, como tipos de dados e interfaces, para tornar o desenvolvimento de aplicativos mais seguro e eficiente.

Esta linguagem foi criada para ajudar a solucionar os problemas comuns enfrentados pelos desenvolvedores JavaScript. Um dos principais problemas é a falta de verificação de tipo. O JavaScript é uma linguagem dinamicamente tipada, o que significa que os tipos de dados são verificados em tempo de execução. Isso pode levar a erros difíceis de detectar, especialmente em projetos maiores e mais complexos.

Além disso, o TypeScript pode ser aplicado tanto no desenvolvimento de aplicações no lado do cliente (frontend) quanto no lado do servidor (backend) por meio do Node.js. Com isso, ele permite a criação de uma stack completa de desenvolvimento em TypeScript, proporcionando uma experiência de desenvolvimento mais consistente e coesa em todas as camadas da aplicação.

Nos últimos anos, o TypeScript se tornou cada vez mais popular entre os desenvolvedores JavaScript e tem sido amplamente adotado em projetos de grande escala.

O que é um superconjunto

Na matemática, um superconjunto é um conjunto que contém todos os elementos de um ou mais conjuntos. Em outras palavras, é um conjunto que possui como elementos todos os elementos de outros conjuntos, além de seus próprios elementos exclusivos.

Por exemplo, considere dois conjuntos A = {1, 2, 3} e B = {3, 4, 5}. Um superconjunto de A e B seria C = {1, 2, 3, 4, 5}, pois C contém todos os elementos de A e B, além de seus próprios elementos exclusivos 4 e 5.

Na programação orientada a objetos, um exemplo de superconjunto seria uma classe B que herda de uma classe A, que por sua vez contém métodos e propriedades próprias. A classe B, como subclasse de A, possui todos os métodos e propriedades de A, além de seus próprios membros exclusivos.

Podemos falar que TypeScript é um superconjunto de JavaScript, pois todo código em JavaScript é aceito em TypeScript, apenas adicionando funcionalidades.

Quais as diferenças entre o TypeScript e o JavaScript?

O TypeScript se diferencia do JavaScript em diversos aspectos, incluindo:

  • Tipagem Estática: O TypeScript é uma linguagem com tipagem estática, o que significa que os tipos de dados das variáveis, parâmetros de função e propriedades de objeto são definidos no momento da declaração. Isso ajuda a evitar erros de digitação e a tornar o código mais seguro e fácil de manter.
  • Inferência de Tipo: O TypeScript pode inferir o tipo de uma variável ou função com base no valor que está sendo atribuído a ela. Isso pode tornar a escrita de código mais rápida e fácil, pois o desenvolvedor não precisa especificar o tipo de dados a todo momento.
  • Interfaces: O TypeScript suporta interfaces, que são contratos que definem a forma de um objeto. As interfaces permitem que os desenvolvedores definam tipos personalizados que podem ser usados ​​para verificar a validade de um objeto e garantir que ele contenha as propriedades necessárias.
  • Strict Null Checking: O TypeScript tem um recurso de verificação de nulos estrito, que ajuda a evitar erros comuns relacionados a valores nulos ou indefinidos. Isso ajuda a tornar o código mais robusto e fácil de depurar.

Quais são os benefícios do TypeScript?

Existem diversos benefícios em utilizar o TypeScript como linguagem de programação, incluindo:

  • Maior Produtividade: O TypeScript oferece recursos avançados, como interfaces, que ajudam a organizar e estruturar o código. Isso pode tornar o processo de desenvolvimento mais eficiente, pois o desenvolvedor pode se concentrar na lógica do negócio em vez de gerenciar detalhes de implementação.
  • Integração com ferramentas de desenvolvimento: O TypeScript é amplamente suportado por ferramentas de desenvolvimento modernas, como o Visual Studio Code, que oferecem recursos avançados de autocompletar, depuração e refatoração. Isso pode tornar a escrita de código mais fácil e rápida.
  • Compatibilidade com JavaScript: O TypeScript é uma extensão do JavaScript e é totalmente compatível com o ecossistema JavaScript. Isso significa que o código JavaScript existente pode ser facilmente integrado com o TypeScript e que os desenvolvedores podem usar bibliotecas e frameworks JavaScript populares em projetos TypeScript.
  • Melhoria da qualidade do código: Com recursos avançados de linguagem, o TypeScript pode ajudar a melhorar a qualidade do código, tornando-o mais legível e fácil de entender. Isso pode tornar o processo de manutenção e depuração mais fácil e eficiente.

Instalação e Configuração

Como instalar o TypeScript?

Antes de começar a trabalhar com o TypeScript, é necessário instalá-lo e configurá-lo em seu ambiente de desenvolvimento.

Para instalar o TypeScript, é necessário ter o Node.js instalado em seu computador. Você pode instalar o Node.js a partir do site oficial ou usando um gerenciador de pacotes, como o Homebrew (no macOS) ou o Chocolatey (no Windows).

Depois de instalar o Node.js, você pode instalar o TypeScript usando o gerenciador de pacotes npm (Node Package Manager). Abra o terminal ou prompt de comando e digite o seguinte comando:

npm install -g typescript

Este comando instalará a versão mais recente do TypeScript globalmente em seu sistema.

Após a instalação, é necessário configurar o TypeScript em seu projeto. É recomendado utilizar o comando "tsc --init" para gerar um arquivo tsconfig.json com as configurações padrões atualizadas em seu projeto, em vez de utilizar um exemplo básico como o apresentado. Isso garantirá que o seu projeto esteja configurado adequadamente e com as opções mais recentes do TypeScript.

Além disso, esse comando irá gerar um arquivo com as configurações adequadas para o seu projeto, evitando possíveis erros de configuração.

Um exemplo básico de um arquivo tsconfig.json é o seguinte:

{
  "compilerOptions": {
    "target": "es6",
    "module": "commonjs",
    "strict": true
  },
  "include": [
    "src/**/*.ts"
  ],
  "exclude": [
    "node_modules"
  ]
}

Este arquivo define as opções do compilador para o TypeScript, incluindo o alvo (target) do compilador, o formato do módulo (module) e as opções de estrita (strict). Ele também define quais arquivos TypeScript devem ser incluídos ou excluídos do processo de compilação.

Depois de configurar o TypeScript em seu projeto, você pode começar a escrever código TypeScript em arquivos .ts e compilar para JavaScript usando o comando tsc no terminal ou prompt de comando.

Como configurar o TypeScript em um projeto?

Inicializar o projeto: Em seguida, navegue até o diretório do projeto no terminal e execute o comando npm init para inicializar o projeto.

Criar um arquivo tsconfig.json: Crie um arquivo chamado tsconfig.json na raiz do diretório do projeto. Este arquivo é responsável por configurar o compilador TypeScript.

Configurar o arquivo tsconfig.json: Dentro do arquivo tsconfig.json, configure as opções desejadas, como a versão do ECMAScript, os diretórios de entrada e saída, e outras opções de compilação.

Por exemplo, o seguinte código configura o TypeScript para compilar os arquivos .ts localizados em uma pasta chamada src e saída na pasta dist.

{
  "compilerOptions": {
    "target": "es6",
    "module": "commonjs",
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true
  },
  "include": [
    "./src/**/*"
  ]
}

Criar um arquivo .ts: Crie um arquivo .ts na pasta de entrada especificada no arquivo tsconfig.json e comece a escrever código TypeScript.

Compilar o arquivo .ts: Execute o comando tsc no terminal para compilar o arquivo .ts. O TypeScript irá gerar um arquivo JavaScript na pasta de saída especificada.

Executar o arquivo .js: Agora que o arquivo TypeScript foi compilado para JavaScript, execute o arquivo gerado na pasta de saída para executar o código.

Com esses passos, você pode configurar o TypeScript em um projeto e começar a escrever código TypeScript.

Lembre-se de que a configuração do TypeScript pode variar dependendo das necessidades do projeto, mas os passos acima são um bom ponto de partida.

Tipos de Dados

Quais são os tipos de dados disponíveis no TypeScript?

O TypeScript suporta os mesmos tipos de dados que o JavaScript, mas com a adição de tipos opcionais e novos tipos avançados. Os tipos de dados disponíveis no TypeScript incluem:

  1. Number: Para representar números, sejam eles inteiros ou de ponto flutuante.
  2. String: Para representar sequências de caracteres.
  3. Boolean: Para representar valores true ou false.
  4. Null e undefined: Para representar valores nulos ou não definidos.
  5. Any: Para representar qualquer tipo de dado.
  6. Void: Para representar a ausência de um valor.
  7. Object: Para representar um tipo de objeto.
  8. Array: Para representar um array de valores de um determinado tipo.
  9. Tuple: Para representar uma matriz com um número fixo de elementos, em que cada elemento pode ter um tipo diferente.
  10. Enum: Para representar um conjunto de valores nomeados.
  11. Union Types: Para representar um tipo que pode ter um dos vários tipos possíveis.
  12. Intersection Types: Para representar um tipo que é a combinação de dois ou mais tipos diferentes.
  13. Type Aliases: Para criar um novo nome para um tipo existente.

Esses são alguns dos tipos de dados disponíveis no TypeScript. Com a tipagem estática fornecida pelo TypeScript, os desenvolvedores podem utilizar esses tipos de dados para escrever código mais seguro e eficiente.

Como definir tipos em variáveis, funções e classes?

Para definir tipos em variáveis, funções e classes no TypeScript, utiliza-se a sintaxe de tipagem estática da linguagem. A seguir, alguns exemplos:

Variáveis:

Para definir o tipo de uma variável, basta declarar o nome da variável seguido por dois pontos e o tipo desejado. Por exemplo:

let minhaVariavel: string;
minhaVariavel = "Olá, mundo!";

No exemplo acima, a variável minhaVariavel é definida como do tipo string.

Também é possível definir o tipo da variável na própria atribuição, sem necessidade de uma declaração prévia. Por exemplo:

let minhaVariavel = "Olá, mundo!";

Neste caso, o TypeScript automaticamente infere o tipo string para a variável minhaVariavel.

Funções:

Para definir os tipos dos parâmetros e do retorno de uma função, basta declará-los entre parênteses, separados por dois pontos. Por exemplo:

function soma(a: number, b: number): number {
  return a + b;
}

No exemplo acima, a função soma recebe dois parâmetros do tipo number e retorna um valor do mesmo tipo, é importante mencionar que o tipo de retorno em uma função é opcional e pode ser omitido caso não haja necessidade de especificar o tipo de valor que será retornado pela função.

Classes:

Para definir os tipos de propriedades e métodos de uma classe, basta declará-los da mesma forma que as variáveis e funções. Por exemplo:

class Pessoa {
  nome: string;
  idade: number;

  constructor(nome: string, idade: number) {
    this.nome = nome;
    this.idade = idade;
  }

  apresentar(): string {
    `Olá, meu nome é ${this.nome} e eu tenho ${this.idade} anos.`;
  }
}

No exemplo acima, a classe Pessoa possui duas propriedades do tipo string e number, respectivamente, e um método que retorna uma string.

Esses são alguns exemplos de como definir tipos em variáveis, funções e classes no TypeScript. A tipagem estática fornecida pelo TypeScript ajuda a evitar erros de tipo comuns e torna o código mais seguro e legível.

Se você desenvolve para web e ainda não conhece TypeScript, não perca mais tempo! TypeScript é um superset do JavaScript que adiciona recursos incríveis à linguagem e ajuda a evitar muitos erros comuns em tempo de desenvolvimento. Assista ao vídeo

Como usar tipos genéricos?

Em TypeScript, tipos genéricos são utilizados para criar funções, classes e interfaces que possam trabalhar com diferentes tipos de dados, sem precisar definir explicitamente esses tipos.

A sintaxe para a criação de um tipo genérico é colocar um nome de tipo entre colchetes angulares (<>). Por exemplo, a seguir temos uma função genérica que retorna um array com elementos do mesmo tipo do argumento passado:

function criarArray<T>(elemento: T, quantidade: number): T[] {
  const array: T[] = [];
  for (let i = 0; i < quantidade; i++) {
    array.push(elemento);
  }
  return array;
}

Nesse exemplo, o tipo genérico T é utilizado como um placeholder para o tipo real que será utilizado na função. A função criarArray recebe um parâmetro elemento do tipo T e um parâmetro quantidade do tipo number. A função cria um array vazio, adiciona quantidade elementos do tipo elemento ao array e, por fim, retorna o array com os elementos.

Para utilizar a função criarArray, basta passar um valor e uma quantidade como argumentos:

const  meuArray = criarArray<number>(10, 5);
console.log(meuArray); // [10, 10, 10, 10, 10]

No exemplo acima, é criado um array com 5 elementos do tipo number, preenchidos com o valor 10,  além disso, é importante destacar que o TypeScript é capaz de inferir o tipo de dados a partir dos argumentos passados para a função. Sendo assim, podemos simplificar a chamada da função e omitir a especificação do tipo, nesse caso, o TypeScript inferirá que o tipo dos elementos do array é number, pois o primeiro argumento passado para a função é do tipo number. Isso torna o código mais limpo e legível.

Além de funções, tipos genéricos também podem ser utilizados em classes e interfaces. A seguir temos um exemplo de uma classe genérica que recebe dois tipos como argumentos e possui um método que retorna uma string com os valores desses tipos:

class Par<A, B> {
  constructor(public primeiro: A, public segundo: B) {}

  toString(): string {
    return `(${this.primeiro}, ${this.segundo})`;
  }
}

let meuPar = new Par<number, string>(10, "abc");
const meuPar = new Par(10, "abc");

No exemplo acima, a classe Par recebe dois tipos genéricos A e B. O construtor da classe recebe dois parâmetros, um do tipo A e outro do tipo B, e os armazena nas propriedades públicas primeiro e segundo. A classe possui também um método toString que retorna uma string com os valores das propriedades.

Para criar uma instância da classe Par, basta passar os tipos desejados como argumentos para o construtor.

Esses são apenas alguns exemplos de como usar tipos genéricos em TypeScript. Com tipos genéricos, é possível escrever código mais flexível e reutilizável, permitindo que funções, classes e interfaces trabalhem com diferentes tipos de dados.

Classes e Interfaces

Como definir classes e interfaces no TypeScript?

No TypeScript, classes e interfaces são definidas usando a mesma sintaxe do JavaScript, mas com algumas adições para suportar recursos específicos do TypeScript, como tipos estáticos, propriedades opcionais.

Para definir uma interface em TypeScript, utiliza-se a palavra-chave interface, seguida pelo nome da interface e um bloco de código entre chaves {} que contém as propriedades e métodos da interface. Por exemplo:

interface Animal {
  nome: string;
  especie: string;
  som(): void;
}

Nesse exemplo, a interface Animal tem duas propriedades: nome e espécie, e um método som() que não retorna nenhum valor.

Para criar uma classe que implemente uma interface, utiliza-se a palavra-chave implements seguida pelo nome da interface. Por exemplo:

class Cachorro implements Animal {
  nome: string;
  especie: string;

  constructor(nome: string) {
    this.nome = nome;
    this.especie = 'Canis lupus familiaris';
  }

  som(): void {
    console.log('Au au!');
  }
}

Nesse exemplo, a classe Cachorro implementa a interface Animal, e portanto, precisa ter as propriedades nome e espécie e o método som() definidos. A classe também tem um construtor que recebe um parâmetro nome e atribui esse valor à propriedade nome.

Como herdar de classes e interfaces?

Herança é um recurso importante em programação orientada a objetos e permite que uma classe ou interface adquira as propriedades e métodos de outra classe ou interface. No TypeScript, é possível herdar de classes e interfaces usando as palavras-chave extends e implements, respectivamente.

Para herdar de uma classe, usa-se a palavra-chave extends seguida pelo nome da classe da qual se deseja herdar. Por exemplo:

class Animal {
  nome: string;

  constructor(nome: string) {
    this.nome = nome;
  }

  emitirSom(): void {
    console.log('Emitindo som...');
  }
}

class Cachorro extends Animal {
  raca: string;

  constructor(nome: string, raca: string) {
    super(nome);
    this.raca = raca;
  }

  latir(): void {
    console.log('Au au!');
  }
}

Nesse exemplo, a classe Cachorro herda da classe Animal usando a palavra-chave extends. A classe Cachorro também tem uma propriedade adicional raca e um método latir().

Para herdar de uma interface, usa-se a palavra-chave implements seguida pelo nome da interface da qual se deseja herdar. Por exemplo:

interface Animal {
  nome: string;
  emitirSom(): void;
}

interface Domesticavel {
  brincar(): void;
}

class Cachorro implements Animal, Domesticavel {
  nome: string;

  constructor(nome: string) {
    this.nome = nome;
  }

  emitirSom(): void {
    console.log('Au au!');
  }

  brincar(): void {
    console.log('Brincando com o dono...');
  }
}

Nesse exemplo, a classe Cachorro implementa as interfaces Animal e Domesticavel usando a palavra-chave implements. A classe precisa ter as propriedades e métodos definidos nas interfaces, no caso, nome, emitirSom() e brincar().

A herança permite que as classes e interfaces compartilhem código, evitando duplicação e tornando o código mais modular e reutilizável. É importante lembrar que no TypeScript, as classes só podem herdar de uma única classe, mas podem implementar várias interfaces.

Como usar modificadores de acesso em classes e interfaces?

Os modificadores de acesso são palavras-chave que permitem controlar o acesso a propriedades e métodos de uma classe ou interface. No TypeScript, existem três modificadores de acesso: public, private e protected.

O modificador public permite que a propriedade ou método seja acessado de qualquer lugar, seja dentro ou fora da classe ou interface. Esse é o modificador padrão, ou seja, se nenhum modificador for especificado, a propriedade ou método será pública. Por exemplo:

class Pessoa {
  public nome: string;
  
  public constructor(nome: string) {
    this.nome = nome;
  }
  
  public exibirNome(): void {
    console.log(this.nome);
  }
}

const pessoa = new Pessoa('João');
console.log(pessoa.nome); // 'João'
pessoa.exibirNome(); // 'João'

O modificador private torna a propriedade ou método acessível apenas dentro da própria classe ou interface. Isso significa que não é possível acessar a propriedade ou método a partir de uma instância da classe ou interface ou de uma subclasse. Por exemplo:

class Pessoa {
  private nome: string;

  constructor(nome: string) {
    this.nome = nome;
  }

  public cumprimentar(): void {
    console.log(`Olá, ${this.nome}!`);
  }
}

const pessoa = new Pessoa('João');
console.log(pessoa.nome); // erro - 'nome' é privado
pessoa.cumprimentar(); // 'Olá, João!'

O modificador protected torna a propriedade ou método acessível dentro da própria classe e de suas subclasses. Isso significa que não é possível acessar a propriedade ou método a partir de uma instância da classe ou interface, mas é possível acessá-los a partir de uma subclasse. Por exemplo:

class Animal {
  protected nome: string;

  constructor(nome: string) {
    this.nome = nome;
  }

  protected emitirSom(): void {
    console.log('Emitindo som...');
  }
}

class Cachorro extends Animal {
  public latir(): void {
    console.log('Au au!');
    this.emitirSom(); // é possível acessar o método protegido
  }
}

const cachorro = new Cachorro('Rex');
console.log(cachorro.nome); // erro - 'nome' é protegido
cachorro.latir(); // 'Au au!' + 'Emitindo som...'

Ao usar modificadores de acesso, é possível controlar quem pode acessar cada parte do código, tornando-o mais seguro e evitando erros de programação.

Módulos e Namespace

Como definir módulos e namespaces no TypeScript?

O TypeScript fornece dois mecanismos para organizar e modularizar o código: módulos e namespaces. Ambos são usados para dividir o código em partes menores e mais gerenciáveis, e para evitar conflitos de nome entre diferentes partes do código.

Módulos

Os módulos são a maneira preferida de organizar o código no TypeScript. Eles são semelhantes aos módulos do ES6, mas com algumas diferenças de sintaxe e comportamento.

Um módulo é um arquivo TypeScript que contém uma ou mais declarações, como classes, funções ou interfaces. Para tornar essas declarações acessíveis a partir de outros arquivos, é preciso exportá-las usando a palavra-chave export. Por exemplo:

// Arquivo modulo.ts
export class Pessoa {
  nome: string;
  
  constructor(nome: string) {
    this.nome = nome;
  }
  
  cumprimentar(): void {
    console.log(`Olá, ${this.nome}!`);
  }
}

Para usar o módulo em outro arquivo, é preciso importá-lo usando a palavra-chave import. Por exemplo:

// Arquivo main.ts
import { Pessoa } from './modulo';

const pessoa = new Pessoa('João');
pessoa.cumprimentar(); // 'Olá, João!'

Os módulos também podem ser exportados com nomes diferentes, usando a palavra-chave as. Isso pode ser útil para evitar conflitos de nome entre diferentes módulos. Por exemplo:

// Arquivo modulo.ts
class Pessoa {
  // ...
}

export { Pessoa as PessoaModulo };

// Arquivo main.ts
import { PessoaModulo } from './modulo';

const pessoa = new PessoaModulo('João');
pessoa.cumprimentar(); // 'Olá, João!'

Namespaces

Os namespaces são uma forma mais antiga de organizar o código no TypeScript. Eles são semelhantes aos módulos, mas com uma sintaxe e um comportamento um pouco diferentes.

Um namespace é uma forma de agrupar declarações relacionadas em um espaço de nomes comum. Para criar um namespace, é preciso usar a palavra-chave namespace, seguida pelo nome do namespace e por um bloco de código que contém as declarações a serem agrupadas. Por exemplo:

// Arquivo namespace.ts
namespace MeuNamespace {
  export class Pessoa {
    // ...
  }
}

// Arquivo main.ts
const pessoa = new MeuNamespace.Pessoa('João');
pessoa.cumprimentar(); // 'Olá, João!'

Os namespaces também podem ser aninhados para criar uma hierarquia de namespaces. Por exemplo:

// Arquivo namespace.ts
namespace MeuNamespace {
  export namespace Classes {
    export class Pessoa {
      // ...
    }
  }
}

// Arquivo main.ts
const pessoa = new MeuNamespace.Classes.Pessoa('João');
pessoa.cumprimentar(); // 'Olá, João!'

Embora os namespaces possam ser úteis em alguns casos, eles têm algumas desvantagens em relação aos módulos, como a falta de suporte a importações de arquivos e a dificuldade de gerenciar dependências entre diferentes namespaces. Por esse motivo, é recomendável usar módulos sempre que possível e reservar os namespaces para casos específicos em que eles são realmente necessários.

Como importar e exportar módulos?

No TypeScript, é possível importar e exportar módulos de diferentes formas, permitindo que o código seja organizado de maneira mais modular e fácil de manter. Existem várias maneiras de importar e exportar módulos no TypeScript, incluindo:

Exportando um módulo

Para exportar uma declaração (por exemplo, uma classe, uma função ou uma constante) de um módulo, é preciso usar a palavra-chave export antes da declaração. Por exemplo:

// Arquivo modulo.ts
export class Pessoa {
  nome: string;
  
  constructor(nome: string) {
    this.nome = nome;
  }
  
  cumprimentar(): void {
    console.log(`Olá, ${this.nome}!`);
  }
}

Isso permite que outras partes do código importem e usem a classe Pessoa a partir deste módulo.

Importando um módulo

Para importar um módulo, é preciso usar a palavra-chave import, seguida do nome do módulo e da(s) declaração(ões) que se deseja importar, entre chaves ({}). Por exemplo:

// Arquivo main.ts
import { Pessoa } from './modulo';

const pessoa = new Pessoa('João');
pessoa.cumprimentar(); // 'Olá, João!'

O nome do módulo entre aspas simples ('./modulo') especifica o caminho relativo para o arquivo do módulo a partir do arquivo atual.

Exportando e importando tudo de um módulo

Também é possível exportar todas as declarações de um módulo usando a palavra-chave export * from. Isso permite que outras partes do código importem todas as declarações do módulo de uma só vez, sem precisar listar cada uma individualmente.

Por exemplo, se tivermos as seguintes declarações em um arquivo modulo.ts:

export class Pessoa {
  // ...
}

export function somar(a: number, b: number): number {
  return a + b;
}

Podemos exportar tudo do módulo usando a seguinte sintaxe:

export * from './modulo';

Isso permite que outras partes do código importem tudo do módulo de uma só vez, usando a seguinte sintaxe:

import * as Modulo from './modulo';

const pessoa = new Modulo.Pessoa('João');
const resultado = Modulo.somar(1, 2);

Isso cria um objeto Modulo que contém todas as declarações exportadas do módulo modulo.ts.

Funções

Como definir funções no TypeScript?

No TypeScript, as funções podem ser definidas de várias maneiras, dependendo do uso e da necessidade do código.

Uma maneira básica de definir uma função é usando a sintaxe padrão do JavaScript:

Como definir funções com parâmetros opcionais e padrão?

function soma(a: number, b: number): number {
  return a + b;
}

Nesse exemplo, temos uma função chamada soma que recebe dois argumentos do tipo number e retorna a soma entre eles, também do tipo number. A diferença em relação ao JavaScript é que usamos a sintaxe a: number e b: number para indicar o tipo de cada parâmetro.

Outra forma de definir funções é usando arrow functions:

const soma = (a: number, b: number): number => {
  return a + b;
}

Nesse caso, a função soma é definida como uma arrow function, que tem a mesma funcionalidade da função padrão anterior, mas com uma sintaxe mais concisa.

Também é possível definir funções como expressões:

const soma = function(a: number, b: number): number {
  return a + b;
}

Nesse caso, a função soma é definida como uma expressão de função, que é semelhante à arrow function, mas tem uma sintaxe ligeiramente diferente.

Além disso, é possível definir funções com parâmetros opcionais e parâmetros padrão:

function saudacao(nome: string, sobrenome = 'Silva', idade?: number) {
  if (idade !== undefined) {
    console.log(`Olá, ${nome} ${sobrenome}. Você tem ${idade} anos.`);
  } else {
    console.log(`Olá, ${nome} ${sobrenome}.`);
  }
}

Nesse exemplo, a função saudacao recebe um parâmetro obrigatório nome, um parâmetro opcional idade e um parâmetro padrão sobrenome (que é definido como 'Silva' caso não seja passado nenhum valor).

Por fim, é importante lembrar que todas as funções podem ser definidas com tipos de retorno explícitos, que indicam qual será o tipo de dado que a função irá retornar:

function soma(a: number, b: number): number {
  return a + b;
}

Nesse exemplo, a função soma tem um tipo de retorno explícito : number, indicando que ela irá retornar um valor do tipo number.

Como usar arrow functions?

Arrow functions são uma forma mais concisa de definir funções no TypeScript. A sintaxe de uma arrow function é simples:

const minhaFuncao = (param1: Tipo, param2: Tipo) => {
  // corpo da função
};

Onde minhaFuncao é o nome da função, param1 e param2 são os parâmetros da função e Tipo é o tipo de dado que cada parâmetro deve receber. O corpo da função é definido dentro das chaves {}.

Se a função tiver apenas uma expressão de retorno, é possível omitir as chaves e a palavra-chave return:

const dobro = (numero: number) => numero * 2;

Nesse exemplo, a função dobro recebe um parâmetro numero do tipo number e retorna o dobro desse número. Como a função tem apenas uma expressão de retorno, as chaves e a palavra-chave return podem ser omitidas.

Arrow functions também são úteis quando precisamos usar funções de ordem superior, como map, filter e reduce. Por exemplo:

const numeros = [1, 2, 3, 4, 5];

const numerosDobrados = numeros.map((numero) => numero * 2);

Nesse exemplo, a função map é usada para criar um novo array com os valores do array original multiplicados por 2. A arrow function (numero) => numero * 2 é passada como argumento para a função map, definindo como cada elemento do array deve ser transformado.

Arrow functions também possuem um escopo léxico, o que significa que elas herdam o valor de this do escopo em que foram definidas. Isso pode ser útil em casos em que precisamos acessar o valor de this dentro da função. Por exemplo:

class MinhaClasse {
  private atributo: number = 0;

  public minhaFuncao() {
    setTimeout(() => {
      this.atributo = 1;
    }, 1000);
  }
}

Nesse exemplo, a função minhaFuncao é definida em uma classe e usa a função setTimeout para atribuir o valor 1 ao atributo atributo depois de 1 segundo. Como a arrow function é usada dentro da função setTimeout, ela herda o valor de this do escopo da classe, permitindo que ela acesse e modifique o valor do atributo.

Decorators

O que são decorators?

Decorators são uma funcionalidade do TypeScript que permitem adicionar metadados a classes, propriedades, métodos e parâmetros de uma forma declarativa. Os decorators são definidos como funções que recebem como argumento a classe ou elemento que deve ser decorado.

Os decorators podem ser usados para diversas finalidades, como adicionar funcionalidades extras a uma classe, validar dados de entrada, registrar informações de debug ou implementar padrões de projeto. Por exemplo, é possível usar decorators para definir propriedades observáveis em uma classe, logar informações de chamadas de método, definir rotas de um serviço REST ou implementar injeção de dependência.

A sintaxe básica de um decorator é a seguinte:

function meuDecorator(elemento: any) {
  // fazer algo com o elemento decorado
}

Onde meuDecorator é o nome da função que define o decorator e elemento é o elemento que deve ser decorado (uma classe, propriedade, método ou parâmetro).

Para aplicar um decorator a um elemento, basta colocar o nome do decorator seguido do elemento, separados por um @, antes da declaração. Por exemplo:

@meuDecorator
class MinhaClasse {
  // conteúdo da classe
}

Nesse exemplo, a classe MinhaClasse é decorada com o decorator meuDecorator.

Os decorators podem ser empilhados, o que significa que é possível aplicar vários decorators a um mesmo elemento. Por exemplo:

@meuDecorator1
@meuDecorator2
class MinhaClasse {
  // conteúdo da classe
}

Nesse exemplo, a classe MinhaClasse é decorada com os decorators meuDecorator1 e meuDecorator2.

Existem vários decorators embutidos no TypeScript, como @deprecated, @sealed, @abstract, @readonly e @classDecorator. Além disso, é possível criar seus próprios decorators para atender às necessidades específicas de um projeto.

Como usar decorators em classes e propriedades?

Para usar decorators em classes e propriedades no TypeScript, basta definir uma função que será o decorator e aplicá-la na classe ou propriedade desejada.

Veja um exemplo de como definir e aplicar um decorator em uma classe:

function meuDecorator(classe: any) {
  console.log("A classe foi decorada!");
}

@meuDecorator
class MinhaClasse {
  // conteúdo da classe
}

Nesse exemplo, a função meuDecorator é definida como um decorator que imprime uma mensagem no console quando aplicada em uma classe. Em seguida, o decorator é aplicado na classe MinhaClasse usando a sintaxe @meuDecorator.

Além disso, é possível definir decorators para propriedades de uma classe. Para isso, é preciso aplicar o decorator na propriedade diretamente. Veja um exemplo:

function minhaPropriedadeDecorator(target: any, key: string) {
  console.log(`A propriedade ${key} foi decorada na classe ${target.constructor.name}`);
}

class MinhaClasse {
  @minhaPropriedadeDecorator
  minhaPropriedade: string;

  constructor(propriedade: string) {
    this.minhaPropriedade = propriedade;
  }
}

Nesse exemplo, a função minhaPropriedadeDecorator é definida como um decorator para a propriedade minhaPropriedade da classe MinhaClasse. Quando a classe é instanciada, o decorator imprime uma mensagem no console informando que a propriedade foi decorada.

Ao aplicar um decorator em uma propriedade, a função que define o decorator recebe dois argumentos: o primeiro é o protótipo da classe (target) e o segundo é o nome da propriedade decorada (key). Dessa forma, é possível acessar e manipular as propriedades da classe dentro do decorator.

É importante lembrar que a ordem em que os decorators são aplicados é da direita para a esquerda. Ou seja, o decorator mais à direita é executado primeiro.

Ferramentas e Integrações

Quais são as ferramentas disponíveis para trabalhar com TypeScript?

Existem diversas ferramentas disponíveis para trabalhar com TypeScript, algumas delas são:

  • Visual Studio Code: é uma IDE gratuita e de código aberto criada pela Microsoft. É uma das melhores opções para desenvolver com TypeScript, pois possui diversas funcionalidades integradas, como linting, depuração, refatoração de código e suporte a TypeScript.
  • Atom: é um editor de código-fonte gratuito e de código aberto que pode ser personalizado com plugins. É uma boa opção para trabalhar com TypeScript, pois oferece suporte para linting e autocompletar código.
  • Sublime Text: é um editor de código-fonte comercial que pode ser personalizado com plugins. Oferece suporte para TypeScript, mas pode ser necessário instalar pacotes adicionais para obter recursos avançados.
  • WebStorm: é uma IDE comercial desenvolvida pela JetBrains. Oferece suporte para TypeScript, com recursos como refatoração de código, depuração e completar código.
  • TypeScript Playground: é uma ferramenta online que permite experimentar com TypeScript sem a necessidade de instalar nada. É uma ótima opção para iniciantes ou para testar pequenos trechos de código.

Além disso, existem outras ferramentas complementares que podem ser usadas com TypeScript, como:

  • Webpack ou Parcel: ferramentas de empacotamento e criação de pacotes para projetos TypeScript.
  • Jest ou Mocha: ferramentas de teste que suportam TypeScript.
  • ESLint ou TSLint: ferramentas de linting para ajudar a manter o código limpo e consistente.

Essas são apenas algumas das ferramentas disponíveis para trabalhar com TypeScript. A escolha das ferramentas a serem utilizadas dependerá do tipo de projeto e das necessidades específicas de cada desenvolvedor.

Como integrar o TypeScript com outros frameworks e bibliotecas?

Integrar o TypeScript com outros frameworks e bibliotecas é uma tarefa relativamente simples, desde que as definições de tipo estejam disponíveis. Aqui estão algumas maneiras de integrar o TypeScript com outros frameworks e bibliotecas populares:

  • React: o React possui um pacote de definições de tipo oficial chamado @types/react. Basta instalá-lo usando o gerenciador de pacotes NPM ou Yarn e, em seguida, usar normalmente com TypeScript. Além disso, o Create React App, uma ferramenta popular para criar projetos React, já vem com suporte para TypeScript.
  • Angular: o Angular é escrito em TypeScript e, portanto, é muito fácil integrar com o TypeScript. O Angular CLI já possui suporte para TypeScript e gera automaticamente definições de tipo para os componentes Angular.
  • Vue: o Vue possui um pacote de definições de tipo oficial chamado @types/vue. Basta instalá-lo usando o gerenciador de pacotes NPM ou Yarn e, em seguida, usar normalmente com TypeScript. Além disso, o Vue CLI já possui suporte para TypeScript.
  • jQuery: o jQuery não possui definições de tipo oficiais, mas existem muitos pacotes de definições de tipo de terceiros disponíveis no repositório DefinitelyTyped. Basta instalá-los usando o gerenciador de pacotes NPM ou Yarn e, em seguida, usar normalmente com TypeScript.
  • Express: o Express é uma biblioteca popular para criar aplicativos web em Node.js. O Express não possui definições de tipo oficiais, mas existem muitos pacotes de definições de tipo de terceiros disponíveis no repositório DefinitelyTyped. Basta instalá-los usando o gerenciador de pacotes NPM ou Yarn e, em seguida, usar normalmente com TypeScript.

Essas são apenas algumas maneiras de integrar o TypeScript com outros frameworks e bibliotecas populares. No entanto, se as definições de tipo não estiverem disponíveis, pode ser necessário criar as próprias definições ou utilizar uma ferramenta como o tsd para gerá-las automaticamente.

Compilação e Depuração

Como compilar código TypeScript?

Para compilar código TypeScript, é necessário ter o compilador do TypeScript instalado. O compilador pode ser instalado globalmente usando o seguinte comando NPM:

npm install -g typescript

Após a instalação do compilador, o próximo passo é criar um arquivo tsconfig.json na raiz do projeto. Este arquivo contém as configurações do compilador do TypeScript para o projeto. Aqui está um exemplo de arquivo tsconfig.json:

{
  "compilerOptions": {
    "target": "es5",
    "module": "commonjs",
    "strict": true,
    "outDir": "./dist"
  },
  "include": [
    "src/**/*"
  ],
  "exclude": [
    "node_modules"
  ]
}

Neste exemplo, o compilador é configurado para produzir código JavaScript ES5 no diretório dist usando o módulo commonjs. A opção strict é definida como verdadeira para habilitar as verificações de tipo mais rigorosas. Os arquivos TypeScript estão incluídos no diretório src e os arquivos do diretório node_modules estão excluídos da compilação.

Depois de configurar o tsconfig.json, podemos compilar o código TypeScript usando o comando tsc. Se o compilador estiver instalado globalmente, podemos executar o seguinte comando na raiz do projeto:

tsc

Isso irá compilar todos os arquivos TypeScript no diretório src e seus subdiretórios e gerar o código JavaScript no diretório dist. Se você quiser compilar um arquivo TypeScript específico, basta executar o comando tsc com o nome do arquivo:

tsc src/app.ts

Este comando irá compilar apenas o arquivo app.ts e gerar o código JavaScript correspondente.

Como depurar código TypeScript?

A depuração de código TypeScript pode ser feita usando ferramentas de depuração padrão do navegador ou usando uma ferramenta de depuração específica do TypeScript, como o VS Code.

Para depurar no navegador, o TypeScript deve ser compilado para JavaScript e o código JavaScript gerado deve estar referenciado no código HTML. É possível configurar o TypeScript para gerar um mapa de origem (source map) durante a compilação, que permitirá que o depurador do navegador mapeie o código JavaScript gerado de volta para o código TypeScript original.

Para gerar um mapa de origem, adicione a opção sourceMap ao arquivo tsconfig.json:

{
  "compilerOptions": {
    "sourceMap": true,
    // outras opções
  },
  // outras configurações
}

Com o mapa de origem habilitado, a depuração pode ser iniciada no navegador abrindo as ferramentas de desenvolvimento do navegador (normalmente pressionando a tecla F12) e definindo pontos de interrupção no código TypeScript. Quando a página for recarregada, o navegador mapeará automaticamente o código JavaScript para o código TypeScript original, permitindo a depuração no código TypeScript.

O VS Code é uma ferramenta de desenvolvimento popular para o TypeScript, e possui um poderoso depurador integrado para o TypeScript. Para usá-lo, abra o projeto TypeScript no VS Code e abra o arquivo TypeScript que deseja depurar. Clique na margem esquerda do editor para definir pontos de interrupção no código. Em seguida, clique no botão "Run and Debug" na barra de ferramentas ou pressione a tecla F5 para iniciar a depuração. Isso iniciará o código no modo de depuração e o VS Code irá parar no primeiro ponto de interrupção definido. O depurador do VS Code possui muitos recursos, como execução passo a passo, observação de variáveis e inspeção de pilha de chamadas.

Melhores práticas

Quais são as melhores práticas para escrever código TypeScript?

Existem diversas boas práticas para escrever código TypeScript que podem ajudar a melhorar a qualidade do código e a torná-lo mais fácil de entender e manter. Algumas dessas práticas incluem:

  • Usar tipos de dados sempre que possível: o TypeScript é uma linguagem de programação tipada, o que significa que a definição de tipos é importante para garantir a consistência do código. Usar tipos de dados em variáveis, parâmetros e retornos de funções ajuda a evitar erros de digitação e garante que o código seja mais fácil de entender.
  • Nomear variáveis e funções com clareza: nomes descritivos ajudam a tornar o código mais legível e fácil de entender. Escolha nomes claros e significativos para variáveis, funções e classes.
  • Usar constantes e enums em vez de literais: o uso de constantes e enums em vez de literais ajuda a tornar o código mais legível e evita a necessidade de escrever o mesmo valor literal várias vezes no código.
  • Evitar tipos implícitos: especificar explicitamente os tipos de dados para variáveis e retornos de funções é uma boa prática que torna o código mais fácil de entender. Evite o uso de tipos implícitos, que podem tornar o código mais difícil de ler e entender.
  • Usar funções de seta em vez de funções anônimas: as funções de seta são mais concisas e têm uma sintaxe mais limpa do que as funções anônimas. Use-as sempre que possível.
  • Usar interfaces para definir contratos: as interfaces ajudam a definir contratos claros para as classes e objetos e garantem que o código seja mais fácil de entender e manter.
  • Usar módulos para organizar o código: os módulos ajudam a organizar o código em arquivos separados e tornam mais fácil reutilizar o código em outros projetos.
  • Usar uma ferramenta de formatação de código: uma ferramenta de formatação de código pode ajudar a manter o código limpo e organizado e garantir que ele siga as convenções de codificação do projeto.
  • Usar uma ferramenta de análise estática de código: uma ferramenta de análise estática de código pode ajudar a detectar possíveis problemas no código antes mesmo de ele ser executado, melhorando a qualidade e a segurança do código.
  • Manter a compatibilidade com o JavaScript: o TypeScript é uma extensão do JavaScript e é importante garantir que o código escrito em TypeScript seja compatível com o JavaScript para que ele possa ser executado em todos os navegadores e ambientes.

Como lidar com erros de compilação?

Os erros de compilação são comuns ao escrever código TypeScript, mas o compilador do TypeScript é muito útil para detectar esses erros e fornecer informações úteis para corrigi-los. Algumas dicas para lidar com erros de compilação no TypeScript incluem:

  • Verificar a sintaxe: muitos erros de compilação no TypeScript são causados por problemas de sintaxe, como uma vírgula ou parêntese ausente. Certifique-se de verificar cuidadosamente a sintaxe do seu código e use uma IDE ou editor de texto que realce a sintaxe para ajudá-lo a encontrar esses erros.
  • Verificar a ortografia: erros de digitação em nomes de variáveis, funções ou classes podem causar erros de compilação. Verifique a ortografia cuidadosamente para garantir que todos os nomes estejam corretos.
  • Usar tipos explícitos: especificar os tipos de dados em suas variáveis, parâmetros de função e valores de retorno pode ajudar a evitar erros de compilação. Use tipos explícitos sempre que possível para ajudar o compilador a detectar erros de tipo.
  • Usar interfaces: as interfaces são úteis para definir contratos entre diferentes partes do seu código. Ao usar interfaces para definir os tipos de dados que suas funções ou classes esperam ou retornam, você pode ajudar a evitar erros de compilação.
  • Usar ferramentas de análise de código: ferramentas de análise de código, como o ESLint, podem ajudar a detectar erros de compilação antes mesmo de executar o código. Configure essas ferramentas para trabalhar com o TypeScript para aproveitar ao máximo suas capacidades.
  • Testar o código frequentemente: teste seu código com frequência para garantir que não haja erros de compilação e para verificar se ele está funcionando corretamente. A execução de testes automatizados pode ajudar a detectar erros de compilação antes que seu código seja implantado em produção.

Conclusão

O TypeScript é uma linguagem de programação que se baseia no JavaScript e adiciona recursos avançados de tipagem e verificação de tipo em tempo de compilação. Ele oferece muitos benefícios para os desenvolvedores, como detecção de erros mais rápida, melhor documentação de código, desenvolvimento mais seguro e manutenção mais fácil do código.

Além disso, é amplamente suportado por muitos frameworks populares, como o React e o Angular, e por várias ferramentas de desenvolvimento, como o Visual Studio Code e o WebStorm.

Se você desenvolve em JavaScript ou está pensando em aprender a desenvolver, vale a pena estudar o TypeScript. Com ele, você poderá escrever um código mais seguro, eficiente e escalável.