Linguagem de Programação

TypeScript

O TypeScript adiciona tipos estáticos ao JavaScript, capturando bugs antes que eles cheguem à produção. Veja o que é, como funciona e como começar a usá-lo hoje.

beginner20 min readUpdated 15 de set. de 2026
typescript
function greet(name: string): string {
  return `Hello, ${name}!`;
}

const message = greet("TypeScript");
console.log(message);
Criado
2012, pela Microsoft
Tipo
Superset estaticamente tipado do JavaScript
Compila para
JavaScript puro
Padrão
ECMAScript com extensões de tipo
Extensão de arquivo
.ts / .tsx
Versão atual
TypeScript 5.x

Por que importa

Por que o TypeScript é importante

Capture bugs precocemente

Erros de tipo são capturados no momento da compilação, não em produção. Você encontra falhas antes mesmo do seu código ser executado.

Melhor experiência no editor

Autocomplete, documentação inline e ferramentas de refatoração funcionam porque o editor entende a estrutura do seu código.

Código autodocumentado

Os tipos descrevem o que as funções esperam e retornam. Ler a assinatura diz exatamente como utilizá-la.

O panorama completo

As três camadas do TypeScript

O TypeScript é uma linguagem, um compilador e um sistema de tipos. Entender em qual camada você está trabalhando torna a depuração muito mais fácil.

TypeScript

A linguagem

JavaScript acrescido de anotações de tipo, interfaces, generics e operações de tipo avançadas.

O compilador

tsc

Converte TypeScript para JavaScript, verifica tipos e gera um output limpo para qualquer target.

O ecossistema

Ferramentas e bibliotecas

Suporte de IDE, arquivos de declaração, strict mode e frameworks que já vêm com TypeScript integrado.

TypeScript em resumo

O que a linguagem oferece

Tipos primitivos

string, number, boolean, null, undefined, symbol, bigint.

Tipos de objeto

Interfaces, type aliases, classes e arrays.

Unions & intersections

Combine tipos com | (ou) e & (e) para modelar dados flexíveis.

Type narrowing

Use typeof, in e discriminantes para permitir que o TypeScript refine os tipos dentro de condicionais.

Generics

Escreva funções e tipos reutilizáveis que funcionam com qualquer tipo, mantendo a segurança de tipos.

Utility types

Transformações integradas como Partial, Pick e Record que modificam tipos existentes.

Uma breve historia

De anotações opcionais ao padrão da indústria

  1. 2012

    Lançamento do TypeScript

    A Microsoft lança o TypeScript como uma camada de tipos opcional para JavaScript.

    12
  2. 2016

    Angular aposta no TypeScript

    O Angular adota o TypeScript como sua linguagem principal, impulsionando a adoção em todo o ecossistema.

    16
  3. 2018

    TypeScript 3.0

    Project references, o tipo unknown e tuples tornam a linguagem mais poderosa.

    18
  4. 2020

    Variadic tuples

    O TypeScript 4.0 traz variadic tuple types e elementos de tupla nomeados.

    20
  5. 2023

    Padronização de Decorators

    Decorators do TC39 chegam ao TypeScript 5.0, alinhando-se à proposta do ECMAScript.

    23
  6. Hoje

    A escolha padrão

    A maioria dos novos projetos JavaScript começa com TypeScript. Frameworks já o trazem nativamente.

    Hoje

O guia completo

TypeScript: Tudo que voce precisa saber

O que é TypeScript?

TypeScript é um superset de JavaScript com tipagem estática que adiciona anotações de tipo opcionais verificadas em tempo de compilação. Ele é compilado para JavaScript puro, portanto, roda em qualquer lugar que o JavaScript rode — navegadores, Node.js, Deno e Bun.

Criado pela Microsoft em 2012, o TypeScript tornou-se a escolha padrão para projetos sérios de JavaScript. O motivo é simples: ele captura bugs antes mesmo do seu código ser executado. Um erro de digitação no nome de uma propriedade, um argumento ausente, um tipo de retorno incorreto — o TypeScript sinaliza esses problemas em tempo de compilação, em vez de permitir que eles se tornem crashes em produção.

A melhor maneira de pensar no TypeScript é como um JavaScript com uma rede de segurança. Você escreve a mesma linguagem que já conhece, mas pode descrever o formato dos seus dados e os contratos de suas funções. O compilador verifica se tudo se encaixa e, em seguida, remove os tipos, deixando apenas o JavaScript limpo.

Por que o TypeScript é importante

O JavaScript é dinamicamente tipado, o que significa que você pode passar qualquer valor para qualquer função e o erro só aparecerá quando esse código for executado. Em um script pequeno, isso não é problema. Em uma aplicação grande com dezenas de desenvolvedores, é a receita para bugs em produção.

O TypeScript resolve isso verificando os tipos antes de o código ser executado:

  • Captura de bugs em tempo de compilação — se você digitar errado o nome de uma propriedade, passar o tipo de argumento incorreto ou esquecer de tratar um valor null, o TypeScript te avisará imediatamente.
  • Melhor experiência no editor — o autocomplete funciona porque o editor sabe quais propriedades um objeto possui. A refatoração é segura porque o compilador encontra todos os lugares que precisam de atualização.
  • Código autodocumentado — as assinaturas das funções descrevem exatamente o que elas esperam e o que retornam. Ler os tipos permite entender como usar o código sem precisar ler a implementação.
  • Refatoração mais segura — altere um tipo e o compilador destacará todos os pontos que quebraram. Chega de caçar erros de runtime por toda a base de código.
  • Adoção progressiva — você pode adicionar TypeScript a um projeto JavaScript existente de forma incremental, um arquivo por vez.

O TypeScript não adiciona overhead de runtime. Os tipos são removidos durante a compilação, portanto, o JavaScript que chega aos usuários é idêntico, independentemente de você ter escrito em TypeScript ou não.

Como o TypeScript funciona

O código TypeScript reside em arquivos .ts. O compilador TypeScript (tsc) lê esses arquivos, verifica os tipos e gera arquivos .js com as anotações de tipo removidas.

// app.ts
function add(a: number, b: number): number {
  return a + b;
}

const result = add(1, 2); // result is number

O compilador verifica se a e b são números, se a função retorna um número e se result recebe um número. Se algo não coincidir, você receberá um erro em tempo de compilação. O JavaScript de saída é simplesmente:

function add(a, b) {
  return a + b;
}
const result = add(1, 2);

Sem tipos, sem overhead. A segurança ocorreu apenas na etapa de build.

Anotações de tipo

As anotações de tipo descrevem o tipo de uma variável, parâmetro ou valor de retorno:

// primitives
let name: string = "Ada";
let age: number = 36;
let active: boolean = true;

// arrays
let skills: string[] = ["HTML", "CSS"];
let scores: Array<number> = [95, 87, 92];

// function parameters and return
function greet(name: string): string {
  return `Hello, ${name}!`;
}

Você nem sempre precisa de anotações. O TypeScript infere os tipos a partir do valor:

let count = 0;          // inferred as number
const items = ["a"];    // inferred as string[]

Prefira a inferência quando o tipo for óbvio. Use anotações quando o tipo não estiver claro a partir do valor, quando você precisar de um tipo mais genérico ou quando a inferência padrão for restrita demais.

Inferência de tipos

O TypeScript deduz automaticamente os tipos a partir do contexto:

// from assignment
let x = 5; // number

// from return value
function doubling(n: number) {
  return n * 2; // inferred as number
}

// from context
const numbers = [1, 2, 3]; // number[]
const doubled = numbers.map(n => n * 2); // number[]

A inferência significa que você escreve menos código mantendo a mesma segurança. A maior parte do código TypeScript depende fortemente de inferência — anote os tipos apenas quando isso adicionar clareza.

Union types

Um union type indica que um valor pode ser de um entre vários tipos:

type Status = "loading" | "success" | "error";

function handleStatus(status: Status) {
  if (status === "loading") {
    // TypeScript knows status is "loading" here
  }
}

let value: string | number;
value = "hello";
value = 42;

Unions modelam bem dados do mundo real. Respostas de API podem ter sucesso ou falhar. Um campo pode ser uma string ou null. Uma função pode aceitar diferentes tipos de entrada.

Type narrowing

O TypeScript realiza o narrowing (estreitamento) de union types dentro de condicionais:

function process(value: string | number) {
  if (typeof value === "string") {
    // TypeScript knows value is string here
    return value.toUpperCase();
  }
  // TypeScript knows value is number here
  return value.toFixed(2);
}

O narrowing também funciona com:

  • Operador in — verifica se uma propriedade existe
  • instanceof — verifica instâncias de classes
  • Verificações de igualdade — === e !==
  • Discriminated unions — verifica uma propriedade de tag comum
type Circle = { kind: "circle"; radius: number };
type Square = { kind: "square"; side: number };
type Shape = Circle | Square;

function area(shape: Shape) {
  switch (shape.kind) {
    case "circle":
      return Math.PI * shape.radius ** 2;
    case "square":
      return shape.side ** 2;
  }
}

Discriminated unions são um dos padrões mais poderosos do TypeScript. A propriedade kind informa ao TypeScript exatamente com qual variante você está lidando.

Interfaces e type aliases

Uma interface descreve a forma de um objeto — suas propriedades, seus tipos e se elas são obrigatórias. Um type alias atribui um nome a qualquer tipo, incluindo unions, tuples, primitivos e tipos de função.

// Interface
interface User {
  id: number;
  name: string;
  email: string;
}

// Type alias with the same shape
type User = {
  id: number;
  name: string;
  email: string;
};

Para formas de objetos simples, os dois são intercambiáveis. As diferenças aparecem quando você compõe ou estende tipos.

Propriedades opcionais e readonly

interface User {
  id: number;
  name: string;
  email?: string;      // optional — string | undefined
  readonly createdAt: Date;
}

const user: User = { id: 1, name: "Ada", createdAt: new Date() };
user.createdAt = new Date(); // Error: cannot assign to a readonly property

Estendendo interfaces

Interfaces herdam de outras interfaces com extends, inclusive de várias ao mesmo tempo:

interface Timestamped {
  createdAt: Date;
  updatedAt: Date;
}

interface User extends Timestamped {
  id: number;
  name: string;
}

Type aliases para unions, tuples e funções

Os type aliases brilham quando o tipo não é um objeto simples:

type ID = string | number;
type Status = "idle" | "loading" | "success" | "error";
type Pair = [string, number];
type Callback = (data: unknown) => void;

Intersection types

Combine tipos com &:

type User = { id: number; name: string };
type WithTimestamp = { createdAt: Date };

type TimestampedUser = User & WithTimestamp;

Interface ou type?

Use interfaces para formas de objetos que possam ser estendidas ou implementadas por classes. Use type aliases para unions, intersections, tuples e tipos de função. Escolha uma convenção para formas de objetos e mantenha a consistência.

Generics

Generics são parâmetros de tipo: eles permitem que uma função, interface ou classe funcione com qualquer tipo, preservando a segurança de tipos (type safety). Em vez de fixar um tipo ou recorrer ao any, você passa uma variável de tipo que é preenchida no momento da chamada.

function first<T>(items: T[]): T | undefined {
  return items[0];
}

const num = first([1, 2, 3]);    // number
const str = first(["a", "b"]);   // string

Sem generics, você teria que usar any e perder as informações de tipo, ou escrever uma função separada para cada tipo.

Funções genéricas

Um parâmetro de tipo é declarado entre colchetes angulares. O TypeScript geralmente o infere a partir dos argumentos, então raramente você precisará passá-lo explicitamente:

function merge<T, U>(a: T, b: U): T & U {
  return { ...a, ...b };
}

const result = merge({ name: "Ada" }, { age: 36 });
// { name: string; age: number }

Interfaces e classes genéricas

interface Response<T> {
  data: T;
  status: number;
}

interface User {
  id: number;
  name: string;
}

const response: Response<User> = {
  data: { id: 1, name: "Ada" },
  status: 200,
};

class Stack<T> {
  private items: T[] = [];
  push(item: T) { this.items.push(item); }
  pop(): T | undefined { return this.items.pop(); }
}

Constraints

Use extends para restringir o que um generic pode aceitar:

interface HasId {
  id: number;
}

function findById<T extends HasId>(items: T[], id: number): T | undefined {
  return items.find((item) => item.id === id);
}

Parâmetros de tipo padrão

interface Paginated<T = unknown> {
  data: T[];
  total: number;
  page: number;
}

const response: Paginated = { data: [1, "a"], total: 10, page: 1 };

Generics são removidos no runtime, portanto não adicionam overhead — eles existem apenas para tornar o compilador e o seu editor mais inteligentes.

O tsconfig.json

O arquivo tsconfig.json configura o compilador TypeScript:

{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "strict": true,
    "outDir": "./dist",
    "rootDir": "./src",
    "esModuleInterop": true,
    "skipLibCheck": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules"]
}

Opções principais:

  • target — qual versão do JavaScript será gerada (ES2020 é um bom padrão)
  • strict — habilita todas as flags de verificação de tipo rigorosa
  • module — o sistema de módulos para a saída (ESNext para projetos modernos)
  • outDir — onde o JavaScript compilado será armazenado
  • esModuleInterop — permite importações default de módulos CommonJS

Sempre use "strict": true para novos projetos. Ele detecta mais bugs, ao custo de exigir um código ligeiramente mais explícito.

Instalando TypeScript

# As a project dependency (recommended)
npm install --save-dev typescript

# Globally (not recommended for teams)
npm install -g typescript

Compile seu projeto:

# Using npx
npx tsc

# Or add a script to package.json
npm run build

Para desenvolvimento com modo watch:

npx tsc --watch

A maioria dos frameworks modernos (Next.js, Vite, Astro) cuida da compilação do TypeScript para você — não é necessário executar tsc manualmente.

Erros comuns

  • Usar any em todo lugar, o que desativa a verificação de tipos.
  • Anotar tudo quando a inferência já conhece o tipo.
  • Não habilitar o modo strict no tsconfig.
  • Confundir tipos do TypeScript com valores de runtime — os tipos são removidos no momento da compilação.
  • Importar tipos incorretamente — use import type para importações apenas de tipos.
  • Ignorar erros do compilador em vez de corrigi-los.
  • Não instalar pacotes @types para bibliotecas JavaScript.
  • Tornar todas as propriedades de uma interface opcionais, o que oculta verificações obrigatórias.
  • Usar any em vez de um generic, descartando a informação de tipo que você queria manter.

Melhores práticas

  • Ative o strict mode desde o primeiro dia.
  • Deixe a inferência fazer o trabalho — anote os tipos apenas quando isso agregar valor.
  • Use unknown em vez de any quando você não souber o tipo.
  • Prefira interfaces para formatos de objetos que possam ser estendidos.
  • Use discriminated unions para máquinas de estado e dados variantes.
  • Mantenha as definições de tipo próximas de onde são utilizadas.
  • Use import type para importações apenas de tipo (type-only imports) para manter o código de runtime reduzido.
  • Trate o compilador como um colega de equipe, não como um obstáculo.

O que aprender a seguir

Agora você já domina os fundamentos do TypeScript: anotações, inferência, unions, narrowing, interfaces, type aliases e generics. A partir daqui, o próximo passo natural são os Advanced Types para aprender sobre utility types, conditional types e mapped types. Escolha um projeto, refatore algum código e deixe a prática consolidar seu conhecimento.

Anotações de tipo

Anotações explícitas ajudam quando o tipo não é óbvio. Deixe a inferência trabalhar quando ele for.

Preferir
const count = 0;
const items: string[] = [];

function add(a: number, b: number) {
  return a + b;
}
Evitar
const count: number = 0;
const items: Array<string> = [];

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

Usando any

O any desativa a verificação de tipos. Use unknown quando não souber o tipo e faça o narrowing.

Preferir
function parse(input: unknown) {
  if (typeof input === "string") {
    return input.toUpperCase();
  }
  return String(input);
}
Evitar
function parse(input: any) {
  return input.toUpperCase();
}

Perguntas frequentes

Perguntas frequentes

Keep learning

Related topics from the roadmap.

$ comecar a aprender

Pronto para aprender TypeScript?

Nosso tutorial interativo te guia por TypeScript passo a passo — com quizzes e codigo real que voce pode executar no navegador.