JavaScript é a linguagem de programação mais amplamente utilizada para desenvolvimento web. Mas ela carece de suporte para verificação de tipos, o que é uma característica essencial das linguagens de programação modernas.

O JavaScript foi originalmente projetado como uma linguagem de script simples. Sua natureza frouxa e ausência de características cruciais de Programação Orientada a Objetos (OOP) apresentam certos desafios para os desenvolvedores:

  1. Documentação e auto-completamento limitados.

  2. Incapacidade de utilizar conceitos de OOP.

  3. Falta de segurança de tipos, resultando em erros em tempo de execução.

  4. Desafios em refatoração e manutenção.

  5. Ausência de interfaces e pontos de integração.

O TypeScript resolve esses problemas. Foi construído para tornar o JavaScript uma linguagem de programação moderna mais perfeita. Ele ajuda a melhorar a experiência do desenvolvedor, oferece muitos recursos úteis e melhora a interoperabilidade.

Este artigo explora os fundamentos do TypeScript. Vou te ensinar como instalar o TS e configurar um projeto. Em seguida, abordaremos alguns conceitos importantes. Você também aprenderá como o TypeScript é compilado para JavaScript, tornando-o compatível com navegadores e ambientes Node.js.

O que vamos abordar:

Pré-requisitos

Antes de mergulhar no TypeScript, é importante ter uma compreensão fundamental de certos conceitos para garantir uma jornada de aprendizado mais suave. Enquanto o TypeScript aprimora o JavaScript com tipagem estática e outros recursos poderosos, ele se baseia nos princípios fundamentais do JavaScript. Aqui está o que você deve saber:

1. Fundamentos do JavaScript

O TypeScript é um superset do JavaScript, o que significa que ele estende as capacidades do JavaScript. Para aprender efetivamente o TypeScript, você deve primeiro ter um sólido entendimento dos conceitos básicos do JavaScript, incluindo:

  • Sintaxe e tipos de dados: Entenda como declarar variáveis (let, const e var), trabalhar com tipos primitivos (strings, números, booleanos) e gerenciar arrays e objetos.

  • Fluxo de controle: Esteja familiarizado com loops (for, while), condicionais (if-else, switch) e como eles controlam a execução do programa.

  • Funções: Saiba como definir e invocar funções, trabalhar com parâmetros, retornar valores e entender conceitos como funções de seta e closures.

  • Programação Orientada a Objetos (POO): Aprenda sobre a criação e o trabalho com objetos, classes e herança. Os recursos baseados em classes do TypeScript são fortemente construídos no modelo de POO do JavaScript.

  • Tratamento de erros: Entenda como usar blocos try-catch para lidar com erros em tempo de execução.

2. HTML e CSS Básicos

Embora o TypeScript seja uma linguagem usada principalmente com JavaScript, ter uma compreensão básica de HTML e CSS é útil, especialmente para desenvolvedores front-end. Isso porque a maioria dos projetos TypeScript envolve a criação ou trabalho com aplicações web

  • HTML: Entenda como estruturar páginas da web usando tags, atributos e elementos.

  • CSS: Aprender a estilizar elementos usando seletores, propriedades e valores. A familiaridade com frameworks CSS como Bootstrap é um bônus.

3. Familiaridade com Ferramentas de Desenvolvimento

  • Um editor de código como o Visual Studio Code, que possui excelente suporte a TypeScript e extensões.

  • Node.js e npm: Compreender como configurar um ambiente de desenvolvimento, executar JavaScript fora do navegador e usar o npm (Node Package Manager) para instalar dependências.

  • Controle de versão (Git): Aprender o básico do Git para rastrear mudanças e colaborar efetivamente em projetos TypeScript.

Começando – Como Instalar TypeScript

Para começar a trabalhar com TypeScript, você precisará instalá-lo. Não é um processo complicado. Com o TypeScript instalado, você pode aproveitar seu poder para criar soluções de alta qualidade.

Você pode instalar o TS de duas maneiras:

  1. Instalação Global: permite acessar o compilador de qualquer diretório em sua máquina. Para instalar o TypeScript globalmente, execute o seguinte comando:
npm install -g typescript

Este comando utiliza o gerenciador de pacotes Node.js, npm. Ele instala o TypeScript globalmente, tornando o comando disponível na linha de comando.

  1. Instalação Local: neste caso, o TypeScript é instalado apenas em um projeto específico. Este método garante compatibilidade de versão e consistência entre os membros da equipe. Para instalar o TypeScript localmente, execute o seguinte comando:
npm install typescript --save-dev

Diferente da instalação global, este comando instala o TypeScript como uma dependência de desenvolvimento. O comando tsc está disponível apenas para uso específico do projeto, ou seja, no projeto específico onde você executa o comando.

Você consegue instalar o TypeScript sem problemas agora? Espero que sim!

Como Organizar Seus Projetos TypeScript

Organizar um projeto TypeScript envolve estruturar seus arquivos com nomes e diretórios significativos, separar preocupações e usar módulos para encapsulamento e reutilização.

A extensão .ts denota arquivos TypeScript e contém código que é convertido em JavaScript para execução.

O TypeScript também suporta arquivos .d.ts, também conhecidos como arquivos de definição de tipo. Esses arquivos oferecem informações de tipo sobre bibliotecas ou módulos JavaScript externos, auxiliando na melhor verificação de tipo, conclusão de código, bem como melhorando a eficiência do desenvolvimento. Abaixo está um exemplo de uma boa estrutura de projeto TS:

my-ts-project/
├── src/ 
│   ├── components/ 
│   │   ├── Button.tsx
│   │   ├── Input.tsx
│   │   └── Modal.tsx
│   ├── services/ 
│   │   ├── api.ts
│   │   └── authService.ts
│   ├── utils/ 
│   │   ├── helpers.ts 
│   │   └── validators.ts
│   ├── models/ 
│   │   ├── User.ts
│   │   └── Product.ts
│   ├── index.tsx 
│   └── styles/ 
│       ├── global.css
│       └── theme.css
├── public/ 
│   ├── index.html
│   └── assets/ 
│       ├── images/
│       └── fonts/
├── tsconfig.json
└── package.json

Vamos entender o que está acontecendo aqui:

  1. src/: Este diretório contém todo o código-fonte do projeto.

    • components/: Contém componentes de IU reutilizáveis (por exemplo, Button, Input, Modal). Usar .tsx (TypeScript JSX) permite escrever JSX com segurança de tipo.

    • services/: Mantém serviços que interagem com APIs externas ou lidam com lógica de aplicativo (por exemplo, api.ts para chamadas de API, authService.ts para autenticação).

    • utils/: Contém funções auxiliares e classes de utilitários para tarefas comuns (por exemplo, helpers.ts para formatação de datas, validators.ts para validação de entrada).

    • models/: Define interfaces ou classes TypeScript para representar estruturas de dados (por exemplo, User.ts, Product.ts).

    • index.tsx: O ponto de entrada principal da aplicação.

    • styles/: Contém arquivos de CSS ou outros estilos.

  2. public/: Este diretório contém ativos estáticos que não são processados pelo TypeScript (por exemplo, HTML, imagens, fontes).

  3. tsconfig.json: O arquivo de configuração do TypeScript, especificando opções do compilador.

  4. package.json: O arquivo manifesto do projeto, listando dependências, scripts e outros metadados do projeto.

Apenas uma nota rápida sobre convenções de nomenclatura para que você as entenda aqui:

  • Use PascalCase para nomes de classes (por exemplo, User, Product).

  • Use camelCase para nomes de funções e variáveis (por exemplo, getUser, firstName).

  • Use nomes significativos e descritivos para arquivos e diretórios.

Esta estrutura promove a modularidade, reutilização e uma melhor organização, tornando seus projetos TypeScript mais fáceis de manter e escalar.

Organizar adequadamente seus projetos TS melhora a manutenção do código, a legibilidade e a colaboração nos fluxos de desenvolvimento TypeScript.

Como os Tipos Funcionam no TypeScript

Assim como qualquer outra linguagem de programação tipada, o TypeScript depende de definições de tipo, geralmente chamadas de Tipagem.

Tipagem é um termo usado na programação para definir tipos de dados para variáveis, parâmetros de método e valores de retorno dentro do código.

A tipagem permite que você identifique erros rapidamente e cedo no desenvolvimento, um superpoder que ajuda a manter uma melhor qualidade de código.

Para especificar um tipo no TypeScript, coloque dois pontos (:) e o tipo de dados desejado após o nome da variável. Aqui está um exemplo:

let age: number = 2;

A variável acima é declarada com o tipo number. No TypeScript, isso significa que ela pode armazenar apenas números e mais nada.

Técnicas de Tipagem

No TypeScript, os dados podem ser tipados de duas maneiras principais:

  1. Tipagem Estática: A tipagem estática refere-se à especificação explícita do tipo de dados de variáveis e outras entidades no código durante o desenvolvimento. O compilador TypeScript faz cumprir essas definições de tipo, ajudando a identificar erros relacionados ao tipo precocemente. Por exemplo:
let age: number = 25;

Aqui, a variável age é explicitamente declarada como tendo o tipo number. Isso garante que apenas valores numéricos possam ser atribuídos a ela, reduzindo o risco de erros em tempo de execução.

  1. Tipagem Dinâmica: A tipagem dinâmica em TypeScript refere-se a cenários onde o tipo de uma variável é determinado em tempo de execução. Isso pode ocorrer quando variáveis são atribuídas ao tipo any, o que permite que elas armazenem valores de qualquer tipo. O TypeScript não realiza verificação de tipos em operações que envolvem variáveis com o tipo any.
let value: any;
value = 25; // Número
value = "Hello"; // String

Embora o TypeScript seja principalmente uma linguagem de tipagem estática, a tipagem dinâmica ainda pode ser útil em casos específicos, como:

  • Trabalhar com bibliotecas de terceiros que não possuem definições de tipo.

  • Interagir com dados estruturados dinamicamente (por exemplo, respostas JSON de APIs com estruturas desconhecidas).

  • Prototipagem rápida ou quando as informações de tipo não estão disponíveis durante a fase inicial de desenvolvimento.

Tipagem Estática vs. Tipagem Dinâmica em TypeScript

A tipagem estática é significativamente mais comum no TypeScript, pois é uma das características principais que diferenciam o TypeScript do JavaScript. Ao impor verificações de tipo estritas, a tipagem estática aprimora a manutenção do código, reduz erros e melhora a produtividade do desenvolvedor.

A tipagem dinâmica é geralmente reservada para casos em que a flexibilidade é necessária ou ao lidar com dados cuja estrutura não pode ser determinada antecipadamente. Apenas tenha em mente que depender muito da tipagem dinâmica (por exemplo, abusar do tipo any) geralmente não é recomendado, pois mina os benefícios do sistema de tipagem estática do TypeScript.

Portanto, enquanto a tipagem dinâmica tem seu lugar em determinados casos específicos, a tipagem estática é a abordagem preferida e mais comumente usada no desenvolvimento em TypeScript.

Inferência de Tipos e Tipos de União

Inferência de Tipos

A inferência de tipos é uma poderosa característica do TypeScript que permite ao compilador deduzir automaticamente o tipo de uma variável com base no valor atribuído a ela durante a inicialização. Em termos mais simples, o TypeScript analisa o valor atribuído a uma variável e decide qual tipo ela deve ser, mesmo que você não declare explicitamente o tipo.

Por exemplo:

typescriptCopyEditlet age = 25; // TypeScript infere que 'age' é do tipo 'number'
age = "hello"; // Erro: O tipo 'string' não é atribuível ao tipo 'number'

Neste exemplo, a variável age é automaticamente inferida como um number devido ao seu valor inicial, 25. Qualquer tentativa de reatribuir age a um valor de um tipo diferente (como uma string) resultará em um erro de tipo.

A inferência de tipo é particularmente útil porque reduz a necessidade de anotações de tipo explícitas, tornando seu código mais limpo e legível. No entanto, ainda fornece a segurança e confiabilidade da verificação de tipos do TypeScript.

Quando usar a inferência de tipo:
  • Atribuições simples: Use a inferência de tipo para atribuições diretas onde o tipo é óbvio a partir do valor.

  • Valores padrão: Ao fornecer valores padrão para variáveis ou parâmetros de função, a inferência de tipo garante que o tipo correto seja aplicado sem exigir anotações manuais.

  • Prototipagem rápida: Durante as primeiras etapas do desenvolvimento, a inferência de tipo pode reduzir o código boilerplate enquanto ainda impõe a segurança de tipo.

Tipos de união

Os tipos de união permitem que uma variável armazene valores de múltiplos tipos. Eles são definidos colocando um pipe (|) entre os tipos. Esse recurso é particularmente útil quando uma variável pode legitimamente ter mais de um tipo durante seu ciclo de vida.

Por exemplo:

typescriptCopyEditlet numOrString: number | string; // 'numOrString' pode conter um número ou uma string
numOrString = 25; // Válido
numOrString = "hello"; // Válido
numOrString = true; // Erro: Tipo 'boolean' não é atribuível ao tipo 'number | string'

Você pode até mesmo definir tipos de união com mais de dois tipos possíveis:

typescriptCopyEditlet multiType: number | string | boolean;
multiType = 42; // Válido
multiType = "TypeScript"; // Válido
multiType = false; // Válido
Quando usar tipos de união:
  • Parâmetros de função flexíveis: Quando uma função pode aceitar vários tipos de entrada.

      typescriptCopyEditfunction printValue(value: string | number) {
        console.log(value);
      }
    
  • Manuseio de diversas estruturas de dados: Ao trabalhar com APIs ou fontes de dados externas onde os campos podem variar em tipo.

  • Variáveis opcionais ou multiestado: Por exemplo, uma variável que pode representar um estado de carregamento como um booleano, um erro como uma string, ou dados válidos como um objeto:

    typescriptCopyEditlet status: boolean | string | { success: boolean; data: any };
    

Como Lidar com Objetos, Arrays e Tipos de Função em TypeScript

Para dominar o TypeScript, é preciso entender os vários tipos de dados suportados no TypeScript e como e quando usá-los.

Os tipos primitivos do JavaScript como strings, números, booleanos, e mais também definem os blocos de construção fundamentais de dados em TypeScript. Mas em particular, Objetos, Arrays, e Funções são essenciais para construir aplicações robustas. Com objetos, arrays e funções, você pode lidar melhor com dados e usá-los de forma eficiente no desenvolvimento.

Tipos de Objetos em TypeScript

Os tipos de objetos representam o modelo para criar objetos em TypeScript. Você pode usar objetos para definir sua forma, de forma semelhante a como as classes são usadas na programação orientada a objetos (OOP). Mas os objetos não possuem os aspectos comportamentais e de encapsulamento que as classes oferecem.

Para definir um tipo de objeto, defina explicitamente o modelo do objeto após os dois pontos (:). Por exemplo:

// Inicialização do Tipo de Objeto

let student: {
    name: string;
    age: number;
    matricNumber: string | number;
 };

// Atribuindo o Objeto com dados reais

student = {
    name: "Akande"
    age: 21,
    matricNumber: 21/52 + "HP" + 19,
};

Observe que as propriedades terminam com um ponto e vírgula ; em vez de uma vírgula ,, o que as finaliza em um objeto real.

O acima é a forma principal de definir um objeto em TypeScript. Outra forma é usar interfaces, as quais abordarei mais tarde neste artigo.

Tipos de Arrays em TypeScript

Arrays em TypeScript permitem que você armazene múltiplos valores do mesmo ou de diferentes tipos de dados em uma única variável. Eles aumentam a segurança e a clareza do seu código, impondo consistência de tipos entre os elementos do array.

Em TypeScript, os tipos de array podem ser definidos de duas maneiras:

1. Usando o Array<type> modelo

Esta sintaxe utiliza o tipo genérico Array, onde type representa o tipo de elementos que o array pode conter.

typescriptCopyEditlet numbers: Array<number> = [1, 2, 3, 4, 5];
let mixedArray: Array<number | string> = [1, 2, 3, 4, 5, "Hello"];
  • numbers Exemplo: Este array pode conter apenas números. Tentar adicionar uma string ou outro tipo a este array resultará em um erro de tipo.

      typescriptCopyEditnumbers.push(6); // Válido
      numbers.push("Hello"); // Erro: Tipo 'string' não é atribuível ao tipo 'number'
    
  • mixedArray Exemplo: Este array utiliza um tipo de união (number | string), permitindo armazenar tanto números quanto strings.

      typescriptCopyEditmixedArray.push(42); // Válido
      mixedArray.push("TypeScript"); // Válido
      mixedArray.push(true); // Erro: O tipo 'boolean' não é atribuível ao tipo 'number | string'
    

2. Utilizando o type[] modelo

Esta sintaxe acrescenta colchetes angulares ([]) ao tipo de elementos que o array pode conter.

typescriptCopyEditconst numbers: number[] = [1, 2, 3, 4, 5];
const mixedArray: (string | number)[] = [1, 2, 3, 4, 5, "Hello"];
  • números Exemplo: Similar ao exemplo Array<number>, este array pode conter apenas números.

      typescriptCopyEditnúmeros[0] = 10; // Válido
      números.push("Olá"); // Erro: O tipo 'string' não é atribuível ao tipo 'number'
    
  • mixedArray Exemplo: Assim como o mixedArray anterior, este array permite tanto números quanto strings, proporcionando flexibilidade onde o tipo de dado pode variar.

      typescriptCopyEditmixedArray[1] = "Mundo"; // Válido
      mixedArray.push(true); // Erro: Tipo 'boolean' não é atribuível ao tipo 'string | number'
    

Como Usar Arrays em TypeScript

Arrays são versáteis e comumente usados para armazenar coleções de dados relacionados. Aqui estão alguns cenários práticos:

Armazenando Dados Homogêneos:
Quando todos os elementos no array compartilham o mesmo tipo, como uma lista de IDs de usuários ou preços de produtos:

typescriptCopyEditconst userIds: number[] = [101, 102, 103];
const productPrices: Array<number> = [29.99, 49.99, 19.99];

Armazenando Dados Heterogêneos:
Quando elementos podem ter diferentes tipos, como uma lista de mensagens contendo texto e metadados opcionais:

typescriptCopyEditconst messages: (string | object)[] = [
  "Welcome",
  { type: "error", text: "Something went wrong" },
];

Iterando Sobre Arrays:
Arrays em TypeScript podem ser usados em loops com total segurança de tipo:

typescriptCopyEditconst scores: number[] = [80, 90, 70];
scores.forEach((score) => console.log(score + 5)); // Adiciona 5 a cada pontuação

Parâmetros de Função e Tipos de Retorno:
Arrays também podem ser passados como parâmetros de função ou retornados por funções com tipagem rigorosa:

typescriptCopyEditfunction getNumbers(): number[] {
  return [1, 2, 3];
}
function printStrings(strings: string[]): void {
  strings.forEach((str) => console.log(str));
}

Tipos de Função em TypeScript

Os tipos de função em TypeScript descrevem a forma das funções, incluindo os tipos de parâmetros e de retorno. Os tipos de função são definidos especificando explicitamente os tipos de parâmetros durante a declaração. O tipo de retorno é especificado adicionando : e o tipo a retornar imediatamente após os colchetes. Por Exemplo:

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

A função acima recebe dois números, os adiciona e retorna um número. A função não funcionará se algum de seus argumentos não for um número e se retornar qualquer outra coisa que não seja um número. Por exemplo:

  1. Chamando a função com uma string como argumento:
// Isso não funcionará porque espera números, e um dos argumentos é uma string

addition(1, "two");
  1. Reescrevendo a função para retornar uma string:
// A função retornará um erro porque está retornando uma string

function addition (a: number, b: number): string {
    let result = a + b;
    let returnStatement = `Addition of ${a} and ${b} is: ${result}`;
    return returnStatement;
}

Teste o código por si mesmo para ver como esses exemplos funcionam.

Entender e lidar efetivamente com objetos, arrays e funções em TypeScript capacita você a escrever código seguro em termos de tipo e de fácil manutenção, melhorando a confiabilidade e escalabilidade de suas aplicações.

Como Criar Tipos Personalizados em TypeScript

Frequentemente, seu padrão de design não segue os tipos de dados integrados em TypeScript. Por exemplo, você pode ter padrões que usam programação dinâmica. E isso pode causar problemas em sua base de código. TypeScript oferece uma solução para criar tipos personalizados para resolver esse problema.

Tipos personalizados permitem que você defina sua estrutura de dados e formatos de acordo com suas necessidades. Isso melhora a legibilidade e a manutenção do código.

A Palavra-chave Type

A palavra-chave type permite que você crie aliases de tipo, oferecendo uma maneira de criar tipos personalizados. Os tipos que você cria podem ser reutilizados em toda a sua base de código. Aliases de tipo ajudam a definir tipos de união ou combinar tipos em aliases únicos. A sintaxe para criar um tipo personalizado é a seguinte:

// Sintaxe

type TypeAlias = type;

E aqui está um exemplo:

O código acima cria um tipo personalizado UserName, uma união de números e strings. Ele usa o tipo criado para definir duas variáveis relativamente para verificar se o tipo funciona.

Observe que é recomendável que um alias de tipo comece com letra maiúscula.

A palavra-chave type é geralmente usada para primitivos – mas e quanto a criar um tipo de objeto personalizado?

É aqui que entram as Interfaces.

Interfaces do TypeScript

As interfaces em TypeScript são usadas para definir a estrutura de objetos. Elas servem como modelos, especificando as propriedades que um objeto deve ter e seus respectivos tipos. Isso garante que os objetos se conformem a uma forma consistente, permitindo segurança de tipo e um código mais claro.

Definindo uma interface

Uma interface é definida usando a palavra-chave interface. A sintaxe é a seguinte:

typescriptCopyEditinterface InterfaceName {
  property1: Type;
  property2: Type;
}

Exemplo:

typescriptCopyEditinterface User {
  id: number;
  name: string;
  email: string;
}

const user: User = {
  id: 1,
  name: "Alice",
  email: "[email protected]",
};

Aqui está o que está acontecendo neste exemplo:

  1. Declaração da interface (interface User):

    • Aqui, definimos um modelo para um objeto User. Ele especifica que qualquer objeto do tipo User deve ter as seguintes propriedades:

      • id do tipo number

      • name do tipo string

      • email do tipo string

  2. Usando a interface (const usuario: User):

    • Declaramos um objeto usuario do tipo User.

    • O objeto deve ter todas as propriedades definidas na interface User, com valores dos tipos especificados. Se uma propriedade estiver faltando ou seu tipo não corresponder, o TypeScript lançará um erro em tempo de compilação.

Por exemplo:

    typescriptCopyEditconst invalidUser: User = {
      id: 1,
      name: "Alice",
      // Erro: A propriedade 'email' está faltando no tipo
    };

Então você pode estar se perguntando – por que você deve usar interfaces?

  • Segurança de tipo: Garante que os objetos sigam a estrutura esperada, prevenindo erros em tempo de execução.

  • Reutilização: A mesma interface pode ser reutilizada em diferentes partes da aplicação, reduzindo a duplicação.

  • Clareza de código: Torna o código mais fácil de ler e entender ao descrever explicitamente a forma dos objetos.

Recursos Avançados de Interfaces

  1. Propriedades opcionais: Você pode tornar as propriedades opcionais adicionando um ponto de interrogação (?).

     typescriptCopyEditinterface Product {
       id: number;
       name: string;
       description?: string; // Propriedade opcional
     }
    
     const product: Product = {
       id: 101,
       name: "Laptop",
     }; // Válido, pois 'description' é opcional
    
  2. Propriedades somente leitura: Use readonly para evitar que propriedades sejam modificadas após a inicialização.

     typescriptCopyEditinterface Point {
       readonly x: number;
       readonly y: number;
     }
    
     const point: Point = { x: 10, y: 20 };
     point.x = 15; // Erro: Não é possível atribuir a 'x' porque é uma propriedade somente leitura
    
  3. Estendendo interfaces: Interfaces podem herdar propriedades de outras interfaces, permitindo composição.

     typescriptCopyEditinterface Person {
       name: string;
       age: number;
     }
    
     interface Employee extends Person {
       employeeId: number;
     }
    
     const employee: Employee = {
       name: "John",
       age: 30,
       employeeId: 1234,
     };
    

Quando Usar Interfaces

Existem vários cenários em que é uma boa ideia usar interfaces. Você pode usá-las quando deseja definir e impor a estrutura de objetos que são passados em seu código.

Elas também são úteis em respostas de API, pois ajudam a verificar o tipo dos objetos recebidos das APIs. Isso garante que os dados estejam de acordo com suas expectativas.

As interfaces também são práticas ao trabalhar com tipos reutilizáveis. Quando várias partes de sua aplicação usam objetos com a mesma estrutura, as interfaces evitam a duplicação.

Ao aproveitar as interfaces, você pode criar aplicações robustas, manuteníveis e seguras em termos de tipo. Elas são um recurso essencial do TypeScript que promove um código limpo e previsível.

Genéricos e Tipos Literais

Genéricos no TypeScript permitem que você crie componentes reutilizáveis que podem trabalhar com vários tipos de dados. Eles permitem que você escreva funções, classes e interfaces sem especificar o tipo exato de antemão, tornando seu código mais flexível e manutenível.

Aqui está um exemplo de uma função genérica e uma interface genérica em TypeScript:

// Interface genérica para uma caixa que pode conter qualquer valor 

interface  Box<T> { 
    value: T; 
}

// Exemplos de uso

let  numberBox: Box<number> = { value: 10 };
let  stringBox: Box<string> = { value: "TypeScript" };

console.log(numberBox.value); // Saída: 10  
console.log(stringBox.value); // Saída: TypeScript

Você pode usar genéricos quando não tiver certeza do seu tipo de dado.

Em contraste com os Genéricos, Tipos Literais permitem que você especifique valores exatos que uma variável pode conter. Isso adiciona maior especificidade e segurança de tipo ao seu código, prevenindo que valores indesejados sejam atribuídos. Aqui está um exemplo:

type Direction = 'up' | 'down' | 'left' | 'right';

Uma variável criada com o tipo acima só pode ser atribuída às strings up, down, left e right.

No geral, aproveitar tipos personalizados em TypeScript capacita você a criar estruturas de dados expressivas, reutilizáveis e seguras em termos de tipo, ajudando você a desenvolver aplicações mais robustas e manuteníveis.

Como Mesclar Tipos em TypeScript

Mesclar tipos em TypeScript combina múltiplas declarações de tipo em um único tipo unificado. Essa capacidade permite que os desenvolvedores construam tipos complexos a partir de peças menores e reutilizáveis, melhorando a clareza, a reutilização e a manutenibilidade do código.

1. Mesclagem de Declarações em Interfaces

TypeScript suporta mesclagem de declarações, onde múltiplas declarações de interface com o mesmo nome são automaticamente combinadas em uma única interface. Isso permite que você aumente uma interface existente definindo propriedades ou métodos adicionais.

Exemplo:
typescriptCopyEditinterface User {
  id: number;
  name: string;
}

interface User {
  email: string;
}

const user: User = {
  id: 1,
  name: "Alice",
  email: "[email protected]",
};
Como funciona:
  • A interface User é declarada duas vezes, cada uma com propriedades diferentes.

  • O TypeScript mescla automaticamente essas declarações em uma única interface:

      typescriptCopyEditinterface Usuário {
        id: number;
        name: string;
        email: string;
      }
    
  • Ao criar o objeto usuário, todas as propriedades da interface mesclada devem estar presentes. Se alguma propriedade estiver faltando, o TypeScript emitirá um erro.

A mesclagem de declarações é particularmente útil ao trabalhar com bibliotecas de terceiros. Você pode estender ou adicionar novas propriedades a uma interface existente sem modificar o código-fonte da biblioteca.

2. Mesclagem de Interfaces Usando a palavra-chave extends

A palavra-chave extends permite que uma interface herde propriedades e métodos de outra, criando uma nova interface que combina as propriedades de ambas.

Exemplo:
typescriptCopyEditinterface Person {
  name: string;
  age: number;
}

interface Employee extends Person {
  employeeId: number;
}

const employee: Employee = {
  name: "John",
  age: 30,
  employeeId: 101,
};
Como funciona:
  • A interface Person define duas propriedades: name e age.

  • A interface Employee usa a palavra-chave extends para herdar as propriedades da Person.

  • A interface Employee também adiciona uma nova propriedade, employeeId.

  • O objeto employee deve incluir todas as propriedades tanto da Person quanto da Employee.

Essa abordagem é ideal para relações hierárquicas. Por exemplo, você pode definir uma interface base para propriedades compartilhadas e estendê-la para tipos especializados.

3. Mesclagem de Tipos Usando o & Operador

O operador &, conhecido como tipo de interseção, permite que você combine múltiplos tipos em um único tipo. O tipo resultante inclui todas as propriedades e métodos de cada tipo.

Exemplo:
typescriptCopyEdittype Address = {
  city: string;
  country: string;
};

type ContactInfo = {
  email: string;
  phone: string;
};

type EmployeeDetails = Address & ContactInfo;

const employee: EmployeeDetails = {
  city: "New York",
  country: "USA",
  email: "[email protected]",
  phone: "123-456-7890",
};
Como funciona:
  • Address e ContactInfo são dois tipos separados.

  • Detalhes do Funcionário é um tipo de interseção criado usando Endereço & Informações de Contato.

  • O objeto funcionário deve incluir todas as propriedades de ambos Endereço e Informações de Contato. Propriedades ausentes ou digitadas incorretamente resultarão em um erro do TypeScript.

Os tipos de interseção são úteis quando você precisa combinar tipos não relacionados ou criar tipos compostos para casos de uso específicos, como respostas de API que mesclam diferentes estruturas de dados.

Quando Usar Cada uma Destas Abordagens

  1. Fusão de declaração: Use quando você deseja estender ou aumentar uma interface existente, particularmente em bibliotecas de terceiros ou bases de código compartilhadas.

  2. extends palavra-chave: Use para relacionamentos hierárquicos onde uma interface base pode ser especializada em tipos mais específicos.

  3. Tipos de interseção (&): Use quando precisar combinar múltiplos tipos não relacionados em um único tipo para casos de uso específicos.

Ao entender essas técnicas de mesclagem e suas implicações, você pode estruturar seu código TypeScript de forma eficaz, melhorando a reutilização e a manutenibilidade enquanto mantém a segurança de tipo.

Empacotamento e Transformações em TypeScript

Nem todos os navegadores suportam o JavaScript mais recente usado pelo TypeScript. Portanto, você pode usar o compilador TypeScript, ou tsc, para converter código TypeScript (.ts files) em JavaScript convencional (.js files) que é universalmente compatível com todos os navegadores. tsc traduz elementos específicos do TypeScript, como tipos e classes, em código JavaScript que os navegadores podem interpretar.

Para executar arquivos TypeScript, tsc é a sua escolha. Você pode instalar tsc usando npm e então transformar seus arquivos .ts em arquivos .js. Para usar tsc, basta especificar o nome do arquivo TypeScript antes do comando tsc. Por exemplo, se você tiver um arquivo chamado app.ts, pode executá-lo digitando:

tsc app.ts

Webpack ou Parcel são frequentemente utilizados para implantar código TypeScript em navegadores. Essas ferramentas empacotam todos os arquivos JavaScript, incluindo os do TypeScript, para melhorar o desempenho e facilitar a implementação de sites. Elas também otimizam o carregamento do código, reduzindo seu tamanho e aumentando a velocidade do navegador.

Construindo um Código Melhor com TypeScript

Aceitar o TypeScript como um desenvolvedor JavaScript abre possibilidades para escrever um código mais robusto e de fácil manutenção. Ao entender os princípios básicos e conceitos fundamentais destacados neste guia, você pode aproveitar o sistema de tipagem estática do TypeScript para detectar erros precocemente no desenvolvimento, resultando em menos bugs e uma manutenção de código mais suave.

Ao utilizar o TypeScript, os desenvolvedores JavaScript podem aprimorar a qualidade e produtividade de seu código. Conforme você continua a explorar e praticar com o TypeScript, descobrirá ainda mais funcionalidades e recursos poderosos.

Continue expandindo seus limites e mergulhe mais fundo no mundo do TypeScript. 😉