TypeScript: o que é, diferenças para o JS e como começar a aprender

TypeScript: o que é, diferenças para o JS e como começar a aprender
Vinicios Neves
Vinicios Neves

Compartilhe

Se você está procurando uma fonte de conhecimento sobre TypeScript, chegou ao lugar certo!

Talvez você só esteja querendo se livrar daqueles erros estranhos que permeiam nossas aplicações:


cannot read properties of undefined (reading 'map')

Ou ouviu falar que TypeScript nos ajuda nas atividades de pessoas que escrevem código. Ou, talvez, está vindo de uma linguagem orientada a objetos, como C# ou Java, e quer se arriscar um pouco com JavaScript.

Ou, então, alguma amizade te falou que TypeScript pode te ajudar a ter menos erros na produção. Se encaixa em alguma das situações?

Bom, vamos dar uma olhada no que o Typescript consegue ajudar:

  • Aumentar a segurança, prevenindo erros comuns;
  • Documentar o código, facilitando a vida do seu eu do futuro ou mesmo das pessoas que vão trabalhar na sua aplicação;
  • Facilitar a refatoração.

E por aí vai! Nesse artigo nós vamos mergulhar fundo nesse universo do TypeScript. Além de entender como ele funciona, vamos analisar e entender as vantagens e desvantagens de uma tipagem mais forte e entender a diferença entre JavaScript e TypeScript.

Vamos lá?

Tipagem segura

Antes de correr e saltar nesse oceano do TypeScript, deixa eu te explicar o que eu quero dizer quando eu digo “tipagem segura”. Bom, Type safety (tipagem segura, em tradução livre) é uma das formas de prevenir que nossas aplicações tentem realizar operações inválidas.

Se liga aqui nos tipos de operações inválidas a que estou me referindo:

  • Multiplicar um número e um array
  • Chamar uma função passando um array de inteiros quando ela precisa de um array de objetos
  • Importar um módulo de um arquivo que não existe

E vou aproveitar essa deixa pra falar um pouco mais sobre JavaScript. Quando coisas desse tipo acontecem, ele tenta se recuperar e fazer "o melhor que consegue", tentando evitar lançar uma exceção.

Vamos pegar alguns cenários para analisar:

Somar o número 42 com um array, o JavaScript retorna uma string '42'

O código na imagem mostra uma operação de soma entre um inteiro 42 e uma lista vazia []

Criar um objeto chamado herói e definir uma propriedade nome para ele ao tentar chamar uma propriedade identidade o JavaScript retorna undefined

A imagem mostra um console de desenvolvedor do navegador Chrome com duas entradas de código JavaScript. Primeiro, uma variável chamada heroi é declarada com let e atribuída a um objeto com uma propriedade nome cujo valor é 'Capitão Codorna'. Após a declaração, o retorno é undefined, que é o comportamento padrão para uma atribuição em JavaScript. A segunda entrada tenta acessar a propriedade identidade do objeto heroi, que não está definida no objeto, resultando novamente em undefined.

Repare que ele não lança uma exceção, mesmo quando tentamos realizar alguma operação inválida (como somar um número com um array).

Analisando aqui rapidamente, podemos dizer que o JS está tentando nos ajudar e evitar lançar uma exceção ao tentar executar uma operação inválida. Ao final, isso não nos ajuda a encontrar e resolver bugs.

Agora quero trazer um ponto de vista diferente. Imagine que, ao tentar somar 42 com um array, o JS lançasse uma exceção do tipo: "Você realmente quer adicionar 42 a um array?" ou "Parece que seu objeto herói não possui uma propriedade chamada identidade. Você se esqueceu de defini-la?"

Pensa comigo: quando que JS nos avisa que cometemos um erro?

Quando executamos nosso script, seja no navegador ou num teste unitário, por exemplo.

No melhor cenário, onde temos uma boa rotina de testes (automatizados e manuais), conseguimos pegar esses erros que acontecem em runtime (tempo de execução, em tradução livre).

Ou seja, sem de fato executar nosso código nós não conseguimos pegar esse tipo de erro. E é aqui que o TypeScript brilha.

Além da vantagem dele nos dar mensagens de erro cheias de significado, é QUANDO ele nos avisa que algo pode estar errado: no nosso editor de código ENQUANTO ESTAMOS CODANDO! Isso deixa meu coração quentinho. :)

Voltando nos nossos exemplos:

Quando tentamos fazer 42 + [] ele diz que o operador + não pode ser aplicado entre os tipos 42 e never[]:

A imagem exibe a interface do editor de código VSCode com um arquivo não salvo aberto, contendo uma linha de código em TypeScript que tenta somar um número 42 com uma lista vazia []. O editor está destacando esta linha com um sublinhado ondulado vermelho, indicando um possível erro no código.

Ele também nos ajuda com objetos e suas propriedades:

A imagem mostra o editor de código VSCode com um script TypeScript. O código contém uma variável heroi com uma propriedade nome, seguida por uma tentativa de acessar uma propriedade inexistente identidade. O VSCode exibe um aviso de erro, indicando que a propriedade identidade não existe no tipo { nome: string; }.

Além de nos ajudar com esses bugs relacionados aos tipos das nossas variáveis, o TS vai de fato mudar a forma com que você escreve código.

Bora então embarcar comigo nessa jornada e transformar o TypeScript no seu melhor amigo?

Banner de divulgação da Imersão IA da Alura em colaboração com o Google. Mergulhe em Inteligência artificial com a Alura e o Google. Serão cinco aulas gratuitas para você aprender a usar IA na prática e desenvolver habilidades essenciais para o mercado de trabalho. Inscreva-se gratuitamente agora!

TypeScript vs JavaScript

Logo do TypeScript vs logo do JavaScript

Essa é a pergunta de milhões e vamos em passos de bebê para ir desmistificando essa comparação. Vamos montar uma tabela e evoluir o pensamento juntos? Vem comigo!

A imagem mostra uma tabela comparando JavaScript e TypeScript: JavaScript vincula tipos dinamicamente, converte tipos automaticamente, verifica tipos em tempo de execução e apresenta erros principalmente em tempo de execução. TypeScript vincula tipos estaticamente, raramente converte tipos automaticamente, verifica tipos em tempo de compilação e apresenta erros principalmente em tempo de compilação.

Como os tipos são vinculados?

Tipar dinamicamente significa que o JavaScript precisa, de fato, executar o programa para entender o tipo das coisas.

O JS simplesmente não sabe o tipo das coisas, antes de rodar o nosso código. Enquanto podemos dizer que o TypeScript é uma linguagem gradualmente tipada. Ou seja, o TS funciona melhor quando ele conhece o tipo de todas as coisas em tempo de compilação, mas não precisa necessariamente conhecer todos os tipos para poder compilar o nosso código.

O TS consegue fazer muitas inferências de tipo e tentar te ajudar a identificar possíveis falhas, mas sem saber o tipo de todas as coisas, a gente perde um grande poder do TypeScript: detectar erros antes da execução do nosso código.

Porém, se você está se perguntando se saber o tipo das coisas é o que faz o TS brilhar, por que ele permitiria cenários sem especificação do tipo?

Bom, isso é muito útil quando a gente está migrando um código de JS para TS, onde ainda não temos tudo devidamente tipado, mas a minha dica pra você é: a não ser que você esteja migrando um código legado, mire sempre em 100% de cobertura de tipagem.

Os tipos são convertidos automaticamente?

O JavaScript vai aplicar um monte de regras para tentar descobrir o que a gente realmente queria e então tentar fazer o melhor que consegue, dada a operação que pedimos pra ele fazer.

Vamos percorrer juntos como o JS executa a instrução 4 + [2]:

  • JS sabe que 4 é um number e [2] é um array
  • Como o operador é o +, ele assume que a gente quer concatenar os dois valores
  • Ele implicitamente converte 4 para uma string: "4"
  • Ele implicitamente também converte [2] para uma string: "2"
  • Concatena o resultado, que vai ser "42"

Se a gente queria mesmo fazer isso, podemos ser mais explícitos, poupando algum trabalho para o JS:


4 + [2]; // "42" 

(4).toString() + [2].toString() // "42"

Enquanto o JS tenta ajudar fazendo conversões pra gente, o TS vai reclamar assim que fizermos algo inválido. O mesmo código em TypeScript seria assim:


4 + [2]; // Error TS2365: Operator '+' cannot be applied to types '4' and 'number[]'.

(4).toString() + [2].toString() // "42"

No segundo caso, onde convertemos tudo para string antes de concatenar, o TS nem reclama de nada. Já se tentarmos fazer algo que tem cara de que está errado, o TS vai reclamar da nossa operação, do nosso código.

Quando os tipos são verificados?

Na maioria dos casos, o JavaScript não está nem aí para os tipos das coisas. Como já comentei antes, ele tenta fazer o melhor possível para converter os tipos e executar a operação solicitada.

O TypeScript, por outro lado, vai verificar nosso código em tempo de compilação, por isso nem precisamos executar a expressão 4 + [2]; para ver a reclamação dele.

Podemos dizer que o TS faz uma análise estática do nosso código, procurando por erros e mostrando eles antes de executar.

É em momentos assim que a gente reclama que "não está compilando" e vamos atrás do que fizemos errado.

Quando os erros são apresentados?

Quando algum erro é lançado durante uma conversão de tipos, o JS faz isso em tempo de execução (na maioria das vezes).

Alguns bugs podem aparecer em tempo de compilação, como por exemplo a declaração de mais de uma variável com o mesmo nome dentro do mesmo escopo, principalmente quando utilizamos ferramentas como o Babel.

Logo, utilizando JavaScript, temos que realmente executar nossa aplicação para ter algum sinal, algum indício de que alguma coisa deu errado.

No melhor dos cenários, pegamos isso com testes de unidade. No pior cenário, a pessoa usuária encontra o bug e fica chateada com a gente. :)

Gif animado do personagem bob sponja e patrick correndo em circulos gritando com uma frase abaixo "We have a bug".

Já o TS sabe lidar com erros de sintaxe e erros de tipagem em tempo de compilação. Na prática, a gente consegue pegar e visualizar esses erros dentro do VSCode (ou outro editor de texto), por exemplo.

Porém, nem tudo são flores, existem vários erros que o TS simplesmente não consegue pegar em tempo de compilação:

  • Perda de conexão com a internet
  • Valores inválidos inseridos pelo usuário
  • Estouro de memória
  • por aí vai :)

Sabendo que o TS não é a bala de prata que vai nos fazer as pessoas desenvolvedoras mais felizes do mundo, mas sim uma excelente ferramenta que vai trazer vários super poderes que não teríamos usando o JS puro, podemos seguir explorando as suas habilidades.

Linha do tempo do TypeScript

Agora, senta que lá vem história! O TypeScript foi desenvolvido e é mantido pela Microsoft. Isso mesmo, a gigante Microsoft!

Eles lançaram a primeira versão lá em outubro de 2012. Mas por que? Bom, conforme os projetos de JavaScript começaram a ficar maiores e mais complexos, as pessoas desenvolvedoras começaram a sentir falta de algumas coisas que ajudariam a gerenciar essa complexidade.

Coisas como tipos estáticos, que podem te dizer se você está fazendo algo errado antes de você gastar horas procurando por um bug (toda nossa conversa anterior sobre tempo de compilação e tempo de execução).

Desde o seu lançamento, o TypeScript tem crescido em popularidade, virando o queridinho de muitas empresas e pessoas desenvolvedoras.

E não é por menos. Ele traz uma série de benefícios que fazem valer a pena adicionar esse passo extra no processo de desenvolvimento. E uma vez que você experimenta o poder da tipagem estática, não tem volta.

Por onde começar? JavaScript ou TypeScript?

Vamos lá, sem mistério: para dançar conforme a música do TypeScript, é bom saber os passos básicos do JavaScript. Afinal, TypeScript nada mais é do que JavaScript com esteroides, certo?

Então, bora entender um pouco sobre cada fase desse processo.

Fundamentos do TypeScript

Sabe aquela história de "tem que aprender a andar antes de correr"? É por aí. O JavaScript é a base, o alicerce. Sem ele, nada feito.

Dominar JavaScript significa que você entende a lógica por trás da programação web, sabe como as coisas funcionam por debaixo dos panos e, mais importante, você consegue se virar em qualquer parada.

É como aprender a cozinhar: você começa com as receitas básicas antes de cozinhar um petit gateau. Então, antes de querer colocar o chapéu de chef, certifique-se de que você sabe o que é um forEach ou como um Promise funciona.

Se você já quiser dar seus primeiros passos e configurar o TS num projeto local, pode dar uma olhada no artigo Conhecendo o TypeScript no Front-end da Monica Hillman.

Decisão de aprendizado baseada em objetivos e projetos

Vou confessar pra você que eu pensei nesse título bonito só pra não colocar aqui: depende. ;)

No fim das contas, a escolha entre JavaScript e TypeScript depende do que você quer alcançar.

Se seu objetivo é construir websites simples, talvez ficar no JavaScript seja o suficiente. Mas se você está de olho em aplicações web complexas, SPAs (Single Page Applications) ou quer trabalhar em grandes projetos em equipe, aprender TypeScript é uma boa.

E lembre-se, não é uma corrida. Aprender programação é uma jornada cheia de descobertas. Se você começar pelo JavaScript, vai ter uma base sólida para quando decidir se aventurar pelo TypeScript.

E se decidir começar pelo TypeScript, vai aprender um monte sobre boas práticas e segurança de tipo desde o início. O importante é escolher um caminho que faça sentido para você e seu momento atual.

TypeScript para iniciantes

Agora bora garantir que a gente tem tudo certo pra começar a codar nossa primeira aplicação com TS?

Ultimamente eu tirei como aprendizado que dividir um objetivo em pequenos passos facilita a minha vida de pessoa desenvolvedora, então além de compartilhar contigo essa minha estratégia, vou definir 3 passos para deixar o seu ambiente de desenvolvimento prontinho:

1. Node.js

Antes de mergulharmos nas águas cristalinas do TypeScript, precisamos garantir que temos o barco certo para a viagem.

Esse barco é o Node.js. Se você ainda não tem o Node.js instalado, não tem problema! Se liga aqui nesse tutorial e manda ver! Quando terminar, vou estar aqui te esperando. ;)

2. TypeScript

Com o Node.js instalado, é hora de trazer o TypeScript para a festa. Abra seu terminal ou prompt de comando e digite:


npm install -g typescript

Esse comando instala o TypeScript globalmente no seu sistema, como se estivéssemos instalando um novo jogo para jogar quando quiser.

3. Editor de código

Se você ainda não tem um favorito, eu super recomendo o Visual Studio Code (VS Code). Ele tem um suporte incrível para TypeScript, autocomplete inteligente, e ainda por cima, gratuito.

E agora que tá tudo no lugar, que tal criar um app de lista de compras usando TS? Vem comigo nesse artigo que eu te acompanho até o fim.

Tipos em TypeScript

Pense nos tipos como se fossem as classes de personagens em um RPG. Cada classe (tipo) tem suas habilidades únicas (propriedades) e limitações. No TypeScript, você define a "classe" de suas variáveis, para que o compilador saiba exatamente o que esperar delas.

Isso evita que você tente, por exemplo, atacar com uma poção ou curar com uma espada.

Vamos ver como isso funciona na prática:


let nome: string = "Gandalf";
let idade: number = 2019;
let poderMagico: boolean = true;

Aqui, nome, idade, e poderMagico são nossos heróis, cada um com sua classe definida: string, number, e boolean.

Assim como não esperamos que Gandalf lance uma magia com uma espada, o TypeScript não nos deixará atribuir um number a uma string, mantendo bugs longe do nosso código.

Agora, imagine que você está montando sua equipe para enfrentar o grande dragão. Você precisa de um guerreiro, um mago, e um arqueiro, cada um com suas habilidades específicas.

As interfaces são como os contratos que esses personagens assinam, prometendo que têm as habilidades (propriedades) necessárias para a jornada.

Por exemplo, vamos definir um mago:


interface Mago {
  nome: string;
  nivel: number;
  lancarFeitico: (feitiço: string) => string;
}

const gandalf: Mago = {
  nome: "Gandalf",
  nivel: 20,
  lancarFeitico: (feitiço: string) => {
    return `Lançando ${feitiço}...`;
  }
};

Com a interface Mago, garantimos que todo mago em nossa equipe tenha um nome, um nivel, e a habilidade de lancarFeitico.

Ou seja, garantimos que sempre que tivermos uma variável que é do tipo Mago podemos executar o método lancarFeitico sem medo que um erro TypeError: "lancarFeitico" is not a function..

Classes

Imagine uma classe como sua receita favorita. Ela tem uma lista de ingredientes (propriedades) e um conjunto de instruções (métodos) sobre como preparar o prato.

No mundo do desenvolvimento com TypeScript, uma classe é um plano para criar objetos que compartilham características e comportamentos comuns.

Por exemplo, se pensarmos em uma receita para fazer um "Bolo", poderíamos ter algo assim:


class Bolo {
  sabor: string;
  camadas: number;

  constructor(sabor: string, camadas: number) {
    this.sabor = sabor;
    this.camadas = camadas;
  }

  servir(): void {
    console.log(`Servindo um bolo de ${this.sabor} com ${this.camadas} camadas.`);
  }
}

Aqui, Bolo é a nossa classe, com sabor e camadas como propriedades e servir como um método para mostrar como o bolo é servido.

Objetos

Depois de definir a receita (Classe), é hora de preparar o prato (Objeto). Usar uma classe para criar um objeto é como seguir uma receita para fazer um bolo na vida real.

Você junta os ingredientes, segue os passos, e voilà, tem um delicioso bolo pronto para ser servido. Vamos criar um bolo de chocolate com três camadas:


let boloDeChocolate = new Bolo("chocolate", 3);
boloDeChocolate.servir();

Neste exemplo, boloDeChocolate é um objeto, uma instância da classe Bolo. Ele tem todas as propriedades (sabor e camadas) e comportamentos (método servir) definidos na classe Bolo.

Generics

Imagine que você está preparando uma série de pratos, cada um com um tipo diferente de carne.

Você tem uma receita maravilhosa que funciona perfeitamente, seja com frango, carne bovina ou peixe, mas você quer garantir que, independentemente do tipo de carne, o prato seja preparado da melhor maneira possível.

Aqui entram os Generics, permitindo que você use a mesma receita (ou método, em termos de programação) para diferentes tipos de ingredientes (tipos de dados).

Generics são como temperos exóticos que você pode adicionar a qualquer prato para realçar seu sabor, mas sem comprometer a essência da receita.

Eles permitem que você crie componentes que funcionam com vários tipos de dados, mantendo a segurança de tipo.

Por exemplo, se tivéssemos uma função para misturar ingredientes em uma tigela, os Generics nos permitiriam definir exatamente que tipo de ingrediente estamos misturando, sem perder a flexibilidade:


function misturar<T>(ingrediente: T): T {
  console.log(`Misturando ${ingrediente} na tigela.`);
  return ingrediente;
}

let frangoMisturado = misturar("frango");
let carneMisturada = misturar(500); // Gramas de carne

Com Generics, a função misturar pode trabalhar com qualquer tipo de ingrediente, sejam strings representando diferentes carnes ou números representando a quantidade.

Enums

Agora, imagine que você está organizando sua despensa e quer categorizar seus ingredientes para facilitar na hora de cozinhar.

Você tem especiarias, grãos, carnes, entre outros. No TypeScript, Enums são como essas categorias, ajudando a organizar valores relacionados sob um mesmo nome de grupo.

Enums permitem que você agrupe valores semelhantes de uma forma que torna seu código mais legível e menos propenso a erros, assim como organizar sua despensa facilita na hora de encontrar o que você precisa.

Por exemplo, se quisermos categorizar tipos de molho para um prato:


enum TipoDeMolho {
  Tomate,
  Pesto,
  Alfredo
}

let meuMolhoFavorito = TipoDeMolho.Pesto;
console.log(`Preparando molho de ${TipoDeMolho[meuMolhoFavorito]}.`);

Aqui, TipoDeMolho é um Enum que categoriza nossos molhos disponíveis. Isso não apenas torna o código mais limpo, mas também evita erros, como misturar tipos de molhos que não existem em nossa despensa.

Modules e namespaces

No contexto do desenvolvimento de software com TypeScript, a utilização de Modules e Namespaces é crucial para a manutenção de um código estruturado e organizado.

Os Modules em TypeScript são entidades de design que permitem a encapsulação de código relacionado dentro de um escopo de arquivo único, facilitando a reusabilidade, a gestão de dependências e a modularidade do projeto.

Cada módulo é essencialmente um arquivo contendo definições de variáveis, funções, classes ou interfaces que podem ser exportadas e importadas por outros modules, permitindo assim uma separação clara e um acoplamento baixo entre as diferentes partes de um aplicativo.

Por outro lado, os Namespaces oferecem uma maneira de agrupar logicamente classes, interfaces, funções e outros modules sob um mesmo nome de domínio.

Eles servem para evitar conflitos de nomenclatura em grandes bases de código e para criar uma camada adicional de organização dentro de um projeto.

Enquanto os Modules se concentram na separação física do código em arquivos ou diretórios distintos, os Namespaces permitem a organização lógica dentro de um mesmo espaço de trabalho ou módulo, podendo conter membros internos marcados com export que são acessíveis externamente.

Modules

Imagine que você está planejando fazer uma série de pratos diferentes para uma grande festa. Cada prato requer um conjunto específico de ingredientes.

Seria incrivelmente conveniente se você pudesse pegar uma embalagem do supermercado que já contém todos os ingredientes para cada prato específico, não é?

No mundo do TypeScript, essas embalagens são os Módulos.

Módulos são arquivos independentes que contêm código relacionado a uma funcionalidade específica. Eles podem ser importados onde necessário, evitando a necessidade de duplicar código. Isso não apenas mantém seu projeto limpo e organizado, mas também facilita a manutenção e a colaboração.

Por exemplo, se você tem um módulo para preparar "Massa de Pizza":


// massaDePizza.ts
export function prepararMassa() {
    console.log("Preparando a massa da pizza.");
}

Em outro arquivo, você pode usar este módulo assim:


import { prepararMassa } from "./massaDePizza";

prepararMassa();

Namespaces

Agora, imagine sua dispensa sem prateleiras, com todos os ingredientes jogados em um grande monte. Seria um pesadelo encontrar o que você precisa, certo?

Da mesma forma, em um grande projeto de desenvolvimento, manter seu código organizado sem alguma forma de estruturação seria caótico. Aqui entram os Namespaces.

Namespaces são uma forma de agrupar logicamente classes, interfaces, funções e outros módulos sob um mesmo nome.

Isso é especialmente útil em aplicações grandes, onde você precisa manter uma organização clara para evitar conflitos de nomes e facilitar a localização de funcionalidades específicas.

Imagine que você tem um Namespace chamado "Ingredientes":


namespace Ingredientes {
    export class Massa {
        static preparar() {
            console.log("Preparando a massa.");
        }
    }

    export class Molho {
        static preparar() {
            console.log("Preparando o molho.");
        }
    }
}

Ingredientes.Massa.preparar();
Ingredientes.Molho.preparar();

Aqui, Massa e Molho são agrupados sob o Namespace Ingredientes, mantendo tudo organizado e acessível.

E não para por aí! Se você quiser mergulhar ainda mais fundo na tipagem do TS, que tal o super artigo do Neilton: Typescript: aprimore aplicações com tipagem estática?

TypeScript Avançado

Explorar os conceitos avançados do TypeScript, como Decorators, Mixins e Mapeamento de Tipos, eleva o desenvolvimento de software a um novo patamar, permitindo uma maior expressividade, reutilização de código e flexibilidade.

Estes recursos avançados oferecem maneiras poderosas de adicionar funcionalidades a classes, objetos e tipos, respectivamente.

Vamos mergulhar em cada um deles para entender como podem ser utilizados para aprimorar nossos projetos TypeScript.

Decorators

Decorators são uma proposta avançada para JavaScript que o TypeScript adotou para permitir uma nova forma de adicionar anotações e uma meta-programação sintática a classes e membros de classe.

Eles são funções especiais que podem ser anexadas a declarações de classe, métodos, acessadores, propriedades e parâmetros para modificar seu comportamento de maneira declarativa.

Um exemplo de Decorator pode ser um @logar que registra informações sobre a chamada de um método:


function logar(target: any, chave: string, descriptor: PropertyDescriptor) {
  const metodoOriginal = descriptor.value;
  descriptor.value = function(...args: any[]) {
    console.log(`Chamando "${chave}" com`, args);
    return metodoOriginal.apply(this, args);
  };
  return descriptor;
}

class Calculadora {
  @logar
  somar(a: number, b: number) {
    return a + b;
  }
}

Neste exemplo, o decorator @logar intercepta a chamada ao método add, registra seus argumentos e a execução do método original.

Mixins

Mixins são uma forma de adicionar funcionalidades a uma classe a partir de múltiplas fontes herdeiras. TypeScript permite criar Mixins através de uma abordagem que combina classes e interfaces para adicionar funcionalidades compostas a classes.

Um exemplo simples de Mixin:


type Construtor<T = {}> = new (...args: any[]) => T;

function ComTimestamp<TBase extends Construtor>(Base: TBase) {
  return class extends Base {
    timestamp = Date.now();
  };
}

class Usuario {}
const UsuarioComTimestamp = ComTimestamp(Usuario);

const usuario = new UsuarioComTimestamp();
console.log(usuario.timestamp); // Mostra o timestamp de quando o objeto foi criado

Este Mixin TimeStamped adiciona uma propriedade timestamp a qualquer classe passada para ele.

Mapeamento de tipos

O Mapeamento de Tipos permite a criação de novos tipos com base em tipos existentes, modificando suas propriedades para torná-las opcionais, somente leitura ou de outros tipos.

É uma ferramenta poderosa para manipular e reutilizar tipos existentes.Um exemplo de Mapeamento de Tipos:


type SomenteLeitura<T> = { readonly [P in keyof T]: T[P]; };

interface Usuario {
  nome: string;
  idade: number;
}

type UsuarioSomenteLeitura = SomenteLeitura<Usuario>;

let usuario: UsuarioSomenteLeitura = { nome: "John", idade: 30 };
// usuario.nome = "Jane"; // Isso resultaria em um erro, pois 'nome' é somente leitura.

Neste exemplo, ReadOnly é um tipo mapeado que torna todas as propriedades do tipo User somente leitura.

Como integrar TypeScript em Projetos Existentes

Integrar TypeScript em projetos existentes pode parecer como tentar trocar os pneus de um carro em movimento, mas não se preocupe!

Com as estratégias certas e um pouco de paciência, você pode transformar essa transição em uma jornada suave e até mesmo divertida.

Vamos explorar como fazer isso sem sobressaltos, mantendo o código rodando e, ao mesmo tempo, melhorando a segurança e a legibilidade.

Estratégias para a migração gradual

Migrar um projeto inteiro para TypeScript de uma só vez pode ser intimidador e, francamente, não é a abordagem mais sábia.

Em vez disso, pense nisso como um jogo de estratégia: você vai conquistando territórios pouco a pouco.

Comece pequeno:

  • Escolha um módulo ou componente de baixa dependência para começar. Isso permite que você se familiarize com TypeScript em uma escala gerenciável.

// Antes: usuario.js

function criaUsuario(nome, idade) {
  return { nome: nome, idade: idade };
}

// Depois: usuario.ts
function criaUsuario(nome: string, idade: number): { nome: string; idade: number } {
  return { nome, idade };
}
  • Integração com JS Existente:

Use a opção "allowJs": true no seu tsconfig.json para permitir que TypeScript e JavaScript coexistam pacificamente no mesmo projeto.

  • Adicione Tipagens Gradualmente:

Aproveite o tipo any para começar a migração sem precisar tipar tudo de uma vez. Depois, refine os tipos aos poucos.

Como lidar com bibliotecas JavaScript em TypeScript

Você vai se deparar com o desafio de usar bibliotecas JavaScript em seu projeto TypeScript. Não tema, pois há ferramentas para ajudar!

  • Definições de tipo: Procure por definições de tipo para suas bibliotecas favoritas no DefinitelyTyped (@types/nome-da-biblioteca). Essas definições adicionam tipagens sem alterar o código da biblioteca em si.

npm install @types/lodash --save-dev
  • Declaração de Módulos: Para bibliotecas sem definições de tipo disponíveis, você pode declarar o módulo você mesmo(a).

// lodash.d.ts
declare module 'lodash' {
  export function find(arr: any[], predicate: Function): any;
}

A Lores escreveu esse artigo super completo sobre esse cenário, então eu recomendo bastante a leitura!

Melhores práticas TypeScript

  • use strict: habilite a flag strict no seu tsconfig.json para aproveitar ao máximo as capacidades de verificação de tipo do TypeScript.

  • Evite o uso excessivo de any: embora tentador, usar any derrota o propósito do TypeScript; use-o com moderação e apenas como uma solução temporária.

  • Aprenda e use tipos avançados: aprofunde-se em tipos avançados e recursos como Generics e Utility Types; eles são poderosos e podem simplificar significativamente seu código.

  • Refatoração gradual: aproveite as ferramentas de refatoração do TypeScript e IDEs compatíveis para fazer mudanças incrementais e seguras em seu código.

Integrar TypeScript em projetos JavaScript existentes é um processo gradual que requer paciência e persistência, mas as recompensas, como um código mais robusto e menos propenso a erros, valem totalmente a pena.

Com as estratégias e práticas recomendadas compartilhadas aqui, você está bem equipado para começar sua aventura.

Compilador TypeScript

O Compilador TypeScript (tsc) é o coração pulsante de qualquer projeto TypeScript. Ele não apenas converte seu código TypeScript para JavaScript, mas também verifica erros de tipo durante o processo de compilação, oferecendo uma camada adicional de segurança antes mesmo que seu código seja executado.

Como funciona o compilador Typescript?

Quando você executa o compilador (usando o comando tsc na linha de comando), ele realiza várias etapas:

  • Análise do código: primeiro, lê e analisa seu código TypeScript, verificando a sintaxe e que todos os tipos estão corretos.
  • Compilação para JavaScript: após a análise, o compilador emite o equivalente JavaScript do seu código TypeScript. Este é o código que será executado nos navegadores ou em ambientes de servidor.
  • Geração de arquivos de declaração (Opcional): se configurado, o compilador também pode gerar arquivos .d.ts, que são arquivos de declaração de tipos. Eles são úteis para quando você quer compartilhar seu código TypeScript como uma biblioteca.

Configurações Personalizadas com tsconfig.json

O poder real do compilador TypeScript vem da sua capacidade de ser altamente configurável. Essas configurações são armazenadas em um arquivo chamado tsconfig.json, que direciona o compilador sobre como o código deve ser compilado.

Algumas das configurações mais comuns incluem:

  • "target": especifica a versão do ECMAScript para a qual o código será compilado (por exemplo, ES5, ES6/ES2015, etc.).
  • "module": define o sistema de módulos a ser usado (como CommonJS, ES6, etc.).
  • "strict": habilita todas as opções estritas de verificação de tipo.
  • "outDir": determina o diretório onde o JavaScript compilado será colocado.
  • "allowJs": permite a compilação de arquivos JavaScript junto com arquivos TypeScript, facilitando a integração em projetos existentes.
  • "noEmitOnError": instrui o compilador a não emitir arquivos de saída se houver erros.

Lembre-se: a documentação do TypeScript é sempre completa e pode ser uma boa fonte de consulta.

Frameworks e Suporte a TypeScript

TypeScript, com sua tipagem estática e recursos avançados, tornou-se uma escolha popular para desenvolvedores(as) que trabalham com frameworks de frontend como Angular e React.

Vamos explorar como TypeScript se integra e potencializar o desenvolvimento nesses dois ecossistemas, garantindo que suas aplicações sejam mais robustas, escaláveis e fáceis de manter.

TypeScript e Angular

Desde sua segunda versão o Angular adotou TypeScript como sua linguagem de escolha, aproveitando suas capacidades de tipagem estática para oferecer uma experiência de desenvolvimento mais coesa e integrada.

Por que Angular & TypeScript?

  • Desenvolvimento orientado a objetos: Angular utiliza intensivamente classes e decoradores, conceitos que são elegantemente suportados em TypeScript, permitindo uma definição clara de componentes, serviços, diretivas, entre outros.

  • Tipagem estática e IntelliSense o uso de TypeScript no Angular não só melhora a verificação de tipo em tempo de compilação, como também potencializa o IntelliSense, proporcionando autocompletar no código, navegação e refatorações mais precisas.

  • Decorators: TypeScript suporta decoradores, que são amplamente utilizados no Angular para definir metadados para classes, propriedades e métodos. Isso simplifica a configuração de componentes Angular e sua integração com o framework.

Exemplo Básico com Angular:


import { Component } from '@angular/core';

@Component({
  selector: 'meu-app',
  template: `<h1>Olá, {{nome}}</h1>`
})
export class AppComponent {
  nome = 'Mundo TypeScript';
}

Este exemplo mostra um componente Angular básico, definido com TypeScript, onde @Component é um decorador que especifica metadados para o componente.

TypeScript e React

React e TypeScript também formam uma combinação poderosa. Enquanto React não foi originalmente projetado com TypeScript em mente (diferentemente do Angular), a comunidade rapidamente adotou TypeScript para desenvolver componentes React devido aos benefícios trazidos pela tipagem estática e segurança de código.

Por que React & TypeScript?

  • Componentes mais seguros: com TypeScript, você pode definir props e estados de componentes com tipos específicos, o que ajuda a prevenir muitos erros comuns em tempo de compilação.

  • Melhor suporte em IDEs: a tipagem estática permite que as IDEs ofereçam um suporte melhor para autocompletar, refatoração e navegação de código.

  • Facilidade de manutenção: em aplicações React grandes, a tipagem estática ajuda a manter o código mais organizado e fácil de entender, especialmente ao trabalhar em equipe.

Exemplo básico com React:


import React from 'react';

interface AppProps {
  nome: string;
}

const App = ({ nome } : AppProps) => {
  return <h1>Olá, {nome}</h1>;
};

export default App;

Este exemplo demonstra um componente funcional React definido com TypeScript, onde AppProps é uma interface que especifica o tipo para as props do componente.

Aprenda mais sobre TypeScript gratuitamente

Acesse gratuitamente as primeiras aulas da Formação Aplique TypeScript no front-end, feita pela Escola de Front-end da Alura e continue aprendendo sobre temas como:

Como aprender melhor? Com Diogo Pires | #HipstersPontoTube

Apostilas da Alura — você profissional em T

Com as Apostilas de tecnologia sobre Front-End, Programação, UX & Design e Ciências de Dados da Alura avance nos estudos e no desenvolvimento da sua carreira em T.

Você poderá se aprofundar nos seguintes tópicos:

  • Desenvolvimento Web com HTML, CSS e JavaScript;
  • UX e Usabilidade aplicados em Mobile e Web;
  • Java para Desenvolvimento Web;
  • Java e Orientação a Objetos;
  • Python e Orientação a Objetos;
  • C# e Orientação a Objetos;
  • SQL e modelagem com banco de dados;

Baixe a apostila completa e as demais apostilas da coleção da Alura em: Apostilas da Alura - Conteúdo livre para o seu aprendizado.

Conclusão

Em resumo, TypeScript oferece um conjunto robusto de funcionalidades que elevam significativamente a experiência de desenvolvimento em comparação ao JavaScript puro.

Ao introduzir tipagem estática, TypeScript não apenas melhora a legibilidade e a manutenibilidade do código, mas também antecipa erros que poderiam ser custosos se identificados apenas em tempo de execução.

Esse sistema de tipos ajuda desenvolvedoras e desenvolvedores a construir aplicações mais complexas e seguras, fornecendo uma documentação implícita e facilitando o refatoramento.

Além disso, TypeScript vem com ferramentas poderosas de análise de código que integram-se perfeitamente a vários editores de texto e ambientes de desenvolvimento, proporcionando uma experiência de desenvolvimento mais produtiva e agradável.

Isso não apenas economiza tempo, mas também aumenta a qualidade do código produzido.

A compatibilidade de TypeScript com JavaScript é outro ponto forte, permitindo que equipes adotem TS gradualmente, sem a necessidade de reescrever todo o código existente de uma só vez.

Isso facilita a migração para projetos já em andamento e permite que desenvolvedoras e desenvolvedores usufruam dos benefícios do TypeScript, independentemente do estágio do projeto.

Enquanto JavaScript continua a ser uma linguagem fundamental para o desenvolvimento web, TypeScript se estabelece como uma poderosa extensão que aprimora essa base, proporcionando ferramentas adicionais para enfrentar os desafios do desenvolvimento moderno.

Em um cenário onde as aplicações se tornam cada vez mais complexas e a demanda por software de alta qualidade nunca foi tão crítica, TypeScript emerge como uma solução valiosa que pode melhorar significativamente nossa experiência como pessoas desenvolvedoras, permitindo-nos construir aplicações mais robustas, confiáveis e de fácil manutenção.

Bibliografia

  • A documentação: https://www.typescriptlang.org/docs/
  • "Programming TypeScript" por Boris Cherny
  • "Effective TypeScript: 62 Specific Ways to Improve Your TypeScript" por Dan Vanderkam
  • "Mastering TypeScript" por Nathan Rozentals
Vinicios Neves
Vinicios Neves

Vinicios é engenheiro de software, envolvido na arquitetura, design e implementação de microsserviços, micro frontends e sistemas distribuídos. Tem experiência significativas em aplicativos, integração e arquitetura corporativa. É Engenheiro de Software pela UNESA e Arquiteto de Software pela PUC Minas.

Veja outros artigos sobre Front-end