Pular para o conteúdo

Introdução ao TypeScript

O que é TypeScript?

TypeScript é uma linguagem de programação fortemente tipada que se baseia no JavaScript. Foi originalmente projetada por Anders Hejlsberg em 2012 e é atualmente desenvolvida e mantida pela Microsoft como um projeto de código aberto.

O TypeScript compila para JavaScript e pode ser executado em qualquer ambiente de execução JavaScript (por exemplo, um navegador ou Node.js em um servidor).

Ele suporta múltiplos paradigmas de programação, como funcional, genérica, imperativa e orientada a objetos, e é uma linguagem compilada (transpilada) que é convertida em JavaScript antes da execução.

Por que TypeScript?

TypeScript é uma linguagem fortemente tipada que ajuda a prevenir erros comuns de programação e a evitar certos tipos de erros em tempo de execução antes que o programa seja executado.

Uma linguagem fortemente tipada permite ao desenvolvedor especificar várias restrições e comportamentos do programa nas definições de tipos de dados, facilitando a capacidade de verificar a correção do software e prevenir defeitos. Isso é especialmente valioso em aplicações de larga escala.

Alguns dos benefícios do TypeScript:

  • Tipagem estática, opcionalmente fortemente tipada
  • Inferência de Tipo
  • Acesso a recursos ES6 e ES7
  • Compatibilidade multiplataforma e entre navegadores
  • Suporte de ferramentas com IntelliSense

TypeScript e JavaScript

Arquivos TypeScript são escritos em arquivos .ts ou .tsx, enquanto arquivos JavaScript são escritos em .js ou .jsx.

Arquivos com a extensão .tsx ou .jsx podem conter a Extensão de Sintaxe JavaScript JSX, que é usada no React para desenvolvimento de UI.

O TypeScript é um superconjunto tipado de JavaScript (ECMAScript 2015) em termos de sintaxe. Todo código JavaScript é código TypeScript válido, mas o inverso nem sempre é verdadeiro.

Por exemplo, considere uma função em um arquivo JavaScript com a extensão .js, como a seguinte:

const sum = (a, b) => a + b;

A função pode ser convertida e usada no TypeScript alterando a extensão do arquivo para .ts. No entanto, se a mesma função for anotada com tipos TypeScript, ela não poderá ser executada em nenhum ambiente de execução JavaScript sem compilação. O seguinte código TypeScript produzirá um erro de sintaxe se não for compilado:

const sum = (a: number, b: number): number => a + b;

O TypeScript foi projetado para detectar possíveis exceções que podem ocorrer em tempo de execução durante o tempo de compilação, fazendo com que o desenvolvedor defina a intenção com anotações de tipo. Além disso, o TypeScript também pode capturar problemas se nenhuma anotação de tipo for fornecida. Por exemplo, o seguinte trecho de código não especifica nenhum tipo TypeScript:

const items = [{ x: 1 }, { x: 2 }];
const result = items.filter(item => item.y);

Neste caso, o TypeScript detecta um erro e informa:

Property 'y' does not exist on type '{ x: number; }'.

O sistema de tipos do TypeScript é amplamente influenciado pelo comportamento de tempo de execução do JavaScript. Por exemplo, o operador de adição (+), que no JavaScript pode realizar a concatenação de strings ou a adição numérica, é modelado da mesma forma no TypeScript:

const result = '1' + 1; // Result is of type string

A equipe por trás do TypeScript tomou a decisão deliberada de sinalizar o uso incomum do JavaScript como erros. Por exemplo, considere o seguinte código JavaScript válido:

const result = 1 + true; // In JavaScript, the result is equal 2

No entanto, o TypeScript lança um erro:

Operator '+' cannot be applied to types 'number' and 'boolean'.

Este erro ocorre porque o TypeScript impõe estritamente a compatibilidade de tipos e, neste caso, identifica uma operação inválida entre um número e um booleano.

Geração de Código TypeScript

O compilador TypeScript tem duas responsabilidades principais: verificar se há erros de tipo e compilar para JavaScript. Esses dois processos são independentes um do outro. Os tipos não afetam a execução do código em um ambiente de execução JavaScript, pois são completamente apagados durante a compilação. O TypeScript ainda pode gerar JavaScript mesmo na presença de erros de tipo. Aqui está um exemplo de código TypeScript com um erro de tipo:

const add = (a: number, b: number): number => a + b;
const result = add('x', 'y'); // Argument of type 'string' is not assignable to parameter of type 'number'.

No entanto, ele ainda pode produzir uma saída JavaScript executável:

'use strict';
const add = (a, b) => a + b;
const result = add('x', 'y'); // xy

Não é possível verificar tipos TypeScript em tempo de execução. Por exemplo:

interface Animal {
name: string;
}
interface Dog extends Animal {
bark: () => void;
}
interface Cat extends Animal {
meow: () => void;
}
const makeNoise = (animal: Animal) => {
if (animal instanceof Dog) {
// 'Dog' only refers to a type, but is being used as a value here.
// ...
}
};

Como os tipos são apagados após a compilação, não há como executar este código em JavaScript. Para reconhecer tipos em tempo de execução, precisamos usar outro mecanismo. O TypeScript fornece várias opções, sendo uma comum a “união tagueada” (tagged union). Por exemplo:

interface Dog {
kind: 'dog'; // Tagged union
bark: () => void;
}
interface Cat {
kind: 'cat'; // Tagged union
meow: () => void;
}
type Animal = Dog | Cat;
const makeNoise = (animal: Animal) => {
if (animal.kind === 'dog') {
animal.bark();
} else {
animal.meow();
}
};
const dog: Dog = {
kind: 'dog',
bark: () => console.log('bark'),
};
makeNoise(dog);

A propriedade “kind” é um valor que pode ser usado em tempo de execução para distinguir entre objetos em JavaScript.

Também é possível que um valor em tempo de execução tenha um tipo diferente daquele declarado na declaração de tipo. Por exemplo, se o desenvolvedor interpretou mal um tipo de API e o anotou incorretamente.

O TypeScript é um superconjunto do JavaScript, portanto a palavra-chave “class” pode ser usada como um tipo e valor em tempo de execução.

class Animal {
constructor(public name: string) {}
}
class Dog extends Animal {
constructor(
public name: string,
public bark: () => void
) {
super(name);
}
}
class Cat extends Animal {
constructor(
public name: string,
public meow: () => void
) {
super(name);
}
}
type Mammal = Dog | Cat;
const makeNoise = (mammal: Mammal) => {
if (mammal instanceof Dog) {
mammal.bark();
} else {
mammal.meow();
}
};
const dog = new Dog('Fido', () => console.log('bark'));
makeNoise(dog);

No JavaScript, uma “classe” tem uma propriedade “prototype”, e o operador “instanceof” pode ser usado para testar se a propriedade prototype de um construtor aparece em qualquer lugar na cadeia de protótipos de um objeto.

O TypeScript não tem efeito no desempenho em tempo de execução, pois todos os tipos serão apagados. No entanto, o TypeScript introduz alguma sobrecarga no tempo de compilação.

JavaScript Moderno Agora (Downleveling)

O TypeScript pode compilar código para qualquer versão lançada do JavaScript desde o ECMAScript 3 (1999). Isso significa que o TypeScript pode transpilar o código dos recursos JavaScript mais recentes para versões mais antigas, um processo conhecido como Downleveling. Isso permite o uso do JavaScript moderno, mantendo a compatibilidade máxima com ambientes de execução mais antigos.

É importante notar que durante a transpilação para uma versão mais antiga do JavaScript, o TypeScript pode gerar código que pode incorrer em uma sobrecarga de desempenho em comparação com as implementações nativas.

Aqui estão alguns dos recursos modernos do JavaScript que podem ser usados no TypeScript:

  • Módulos ECMAScript em vez de callbacks “define” no estilo AMD ou instruções “require” do CommonJS.
  • Classes em vez de protótipos.
  • Declaração de variáveis usando “let” ou “const” em vez de “var”.
  • Loop “for-of” ou “.forEach” em vez do loop “for” tradicional.
  • Funções de seta (Arrow functions) em vez de expressões de função.
  • Atribuição via desestruturação (Destructuring assignment).
  • Nomes de propriedade/método abreviados e nomes de propriedade computados.
  • Parâmetros de função padrão.

Ao aproveitar esses recursos modernos do JavaScript, os desenvolvedores podem escrever códigos mais expressivos e concisos no TypeScript.