Cheatsheet TypeScript
JavaScript com tipagem estática
TypeScript
Tipos Básicos
Tipos primitivos
let nome: string = "Ana"; let idade: number = 30; let ativo: boolean = true; let nada: null = null; let indef: undefined = undefined;
Os tipos primitivos são string, number e boolean, mais null e undefined. A anotação : tipo é opcional quando o valor permite inferência.
Union types
let id: string | number;
id = "abc";
id = 123;
function format(v: string | number): string {
return String(v);
}Um union type permite que um valor seja de vários tipos, separados por |. Dentro da função só podes usar operações comuns a todos; para operações específicas faz narrowing.
const assertion (as const)
const config = {
host: "localhost",
porta: 3000,
} as const;
// config.porta = 4000; // ERRO: readonly
type Portas = typeof config.porta; // 3000O as const torna o valor profundamente imutável e converte os tipos em literais (3000 em vez de number). É útil para constantes e para criar tipos a partir de dados.
Arrays e tuplos
let nums: number[] = [1, 2, 3]; let textos: Array<string> = ["a", "b"]; let tuplo: [string, number] = ["Ana", 30]; tuplo[0]; // string
Arrays tipam-se com tipo[] ou Array<tipo>. Um tuplo é um array com tamanho e tipos fixos por posição, útil para pares de valores.
Literal types
let dir: "esquerda" | "direita"; dir = "esquerda"; type Status = "ativo" | "inativo"; let s: Status = "ativo";
Um literal type restringe a variável a valores exatos. Combinado com unions cria conjuntos fechados de opções, mais seguros que string. Ideal para estados e configurações.
typeof e instanceof
function area(v: number | Date) {
if (typeof v === "number") {
return v * v; // number
}
return v.getTime(); // Date
}Os operadores typeof (para primitivos) e instanceof (para classes) fazem narrowing: dentro do if o TypeScript afunila o tipo automaticamente, permitindo operações específicas.
any, unknown, never, void
let x: any = "qualquer"; // sem verificação
let y: unknown = 42; // seguro, exige check
function erro(): never {
throw new Error("falha");
}
function log(): void { } // sem retornoO any desativa a verificação (evita-o). O unknown aceita qualquer valor mas obriga a verificar antes de usar. never é para funções que nunca retornam e void para as que não devolvem valor.
Optional e null
interface User {
nome: string;
email?: string; // opcional
}
let valor: string | null = null;
let def: string = valor ?? "padrão";O ? marca uma propriedade opcional (pode ser undefined). O operador ?? (nullish coalescing) devolve o valor à direita se o da esquerda for null ou undefined.
Objetos tipados
let ponto: { x: number; y: number } = {
x: 1,
y: 2,
};
let user: { nome: string; idade?: number };Podes tipar objetos diretamente com { campo: tipo }. Cada propriedade é obrigatória salvo se marcada com ?. Para formas reutilizadas, prefere uma interface ou type.
Inferência de tipo
let nome = "Ana"; // string
let nums = [1, 2, 3]; // number[]
const obj = { x: 1, y: 2 };
// nome = 10; // ERRO: não é numberO TypeScript infere o tipo a partir do valor inicial, por isso muitas anotações são dispensáveis. Variáveis const têm tipos mais específicos. Deixa o compilador inferir sempre que possível.
Type assertion (as)
let el = document.getElementById("app") as HTMLDivElement;
let n = ("123" as string).length;
let input = document.querySelector("input")!;A type assertion com as diz ao compilador para tratar um valor como um tipo específico, sem verificação. Usa-a só quando sabes mais que o TypeScript. Não converte o valor em runtime.
bigint e symbol
let grande: bigint = 9007199254740991n;
let id: symbol = Symbol("id");
let chave: unique symbol = Symbol("chave");O bigint representa inteiros arbitrariamente grandes (sufixo n) e o symbol cria identificadores únicos. unique symbol é um tipo único associado a uma constante.
Funções
Função tipada
function somar(a: number, b: number): number {
return a + b;
}
const dobro = (x: number): number => x * 2;Tipa os parâmetros e o retorno de uma função. O retorno muitas vezes é inferido, mas anotá-lo documenta o contrato. Funções arrow seguem a mesma regra.
Tipo de função
type Callback = (data: string) => void;
function fetchDados(cb: Callback) {
cb("resultado");
}
fetchDados((d) => console.log(d));Podes definir um tipo de função com type, descrevendo parâmetros e retorno. É reutilizável em várias assinaturas e torna os callbacks mais legíveis e seguros.
Retorno void vs undefined
function log(msg: string): void {
console.log(msg);
}
function achar(id: number): string | undefined {
return undefined;
}Um retorno void significa que a função não devolve nada útil. Já undefined como tipo de retorno indica que pode devolver esse valor explicitamente. São conceitos distintos.
Parâmetros opcionais e default
function saudar(nome: string, titulo?: string) {
return titulo ? `${titulo} ${nome}` : nome;
}
function conectar(host: string = "localhost") { }Um parâmetro opcional marca-se com ? e pode ser undefined. Um parâmetro com valor padrão usa = valor e já é opcional na chamada. Os opcionais devem vir depois dos obrigatórios.
Arrow function tipada
const multiplicar: (a: number, b: number) => number =
(a, b) => a * b;
const itens = [1, 2, 3].map((n: number) => n * 2);Em arrow functions, o tipo declara-se na variável com a sintaxe (params) => retorno. Os parâmetros da implementação ficam inferidos. Em .map() o tipo do item é inferido do array.
Função genérica simples
function identidade<T>(valor: T): T {
return valor;
}
identidade<string>("olá");
identidade(42); // T inferido como numberUma função genérica usa <T> para aceitar qualquer tipo mantendo a relação entre entrada e saída. O tipo pode ser explícito ou inferido. É a base da programação reutilizável.
Rest parameters
function soma(...nums: number[]): number {
return nums.reduce((a, b) => a + b, 0);
}
soma(1, 2, 3, 4); // 10O rest parameter ...nome: tipo[] aceita um número variável de argumentos, tratados como um array tipado. Deve ser o último parâmetro da função.
Callbacks tipados
type Handler = (evento: string, codigo: number) => void;
function registar(h: Handler) {
h("click", 200);
}
registar((ev, cod) => console.log(ev, cod));Ao passar callbacks, tipa-os para que os parâmetros fiquem inferidos no uso. Assim o compilador verifica os argumentos e o retorno, evitando erros em funções de ordem superior.
Function overloads
function parse(v: string): number;
function parse(v: number): string;
function parse(v: string | number) {
return typeof v === "string"
? parseInt(v) : String(v);
}Os overloads definem várias assinaturas para a mesma função. As primeiras linhas declaram os casos e a última é a implementação. O compilador escolhe a assinatura certa conforme o argumento.
this tipado
interface Elemento {
nome: string;
mostrar(this: Elemento): void;
}
function mostrar(this: Elemento) {
console.log(this.nome);
}O TypeScript permite tipar o this como um pseudo-parâmetro na primeira posição. Não conta como argumento real, mas ajuda o compilador a verificar o contexto de chamada.
Interfaces e Types
Interface básica
interface Pessoa {
nome: string;
idade: number;
email?: string; // opcional
}
const p: Pessoa = { nome: "Ana", idade: 30 };Uma interface define a forma de um objeto: nomes e tipos das propriedades. O ? marca campos opcionais. É a forma mais comum de tipar objetos em TypeScript.
Index signature
interface Dict {
[key: string]: number;
}
const notas: Dict = { math: 95, port: 88 };
type Mapa = Record<string, number>;Uma index signature [key: string]: tipo descreve objetos com chaves dinâmicas. O utilitário Record<string, tipo> é a forma moderna e concisa de fazer o mesmo.
Declaration merging
interface Janela {
titulo: string;
}
interface Janela {
largura: number;
}
// Janela tem titulo E larguraO declaration merging junta automaticamente interfaces com o mesmo nome numa só. É útil para estender tipos de bibliotecas. Só funciona com interface, não com type.
type alias
type Ponto = {
x: number;
y: number;
};
type ID = string | number;O type cria um alias para qualquer tipo, incluindo objetos, unions e primitivos. Ao contrário da interface, aceita unions e tipos compostos. Para objetos simples são quase equivalentes.
Readonly
interface Config {
readonly host: string;
readonly porta: number;
}
const cfg: Config = { host: "x", porta: 80 };
// cfg.host = "y"; // ERROO modificador readonly torna uma propriedade imutável após a criação. Tentar reatribuir causa erro de compilação. Para arrays usa readonly tipo[] ou ReadonlyArray.
Extends múltiplo
interface TemId { id: number; }
interface TemData { criadoEm: Date; }
interface Entidade extends TemId, TemData {
nome: string;
}Uma interface pode estender várias interfaces de uma vez, separadas por vírgulas. A interface resultante herda todos os campos. É uma forma limpa de compor contratos reutilizáveis.
interface vs type
// interface: extensível, merging
interface A { x: number; }
interface A { y: number; } // merge
// type: unions, mapeados
type B = string | number;Usa interface para contratos de objetos públicos (permite declaration merging e extends) e type para unions, tuplos e tipos derivados. Ambos funcionam para objetos.
Interface para função
interface Comparador {
(a: number, b: number): number;
}
const cmp: Comparador = (a, b) => a - b;Uma interface pode descrever uma assinatura de função com (params): retorno. Os parâmetros da implementação ficam inferidos. É uma alternativa ao type de função.
Type derivado de objeto
const config = {
host: "localhost",
porta: 3000,
} as const;
type Config = typeof config;O operador typeof em posição de tipo cria um tipo a partir de um valor existente. Combinado com as const, gera tipos literais precisos. Evita duplicar a definição.
Extends
interface Animal {
nome: string;
}
interface Cao extends Animal {
raca: string;
}
const c: Cao = { nome: "Rex", raca: "Labrador" };O extends cria uma interface que herda os campos de outra e adiciona os seus. A interface filha inclui todas as propriedades da pai. É a forma de compor contratos.
Intersection types (&)
type Nome = { nome: string };
type Idade = { idade: number };
type Pessoa = Nome & Idade;
const p: Pessoa = { nome: "Ana", idade: 30 };Um intersection type combina vários tipos com &, exigindo as propriedades de todos. É o oposto do union: em vez de "um ou outro", é "todos em conjunto".
Classes
Classe com tipos
class Pessoa {
nome: string;
private idade: number;
constructor(nome: string, idade: number) {
this.nome = nome;
this.idade = idade;
}
}Numa class, declara os campos com os seus tipos antes do construtor. O TypeScript verifica as atribuições em this. Todos os campos obrigatórios devem ser inicializados.
Implements
interface Serializable {
toJSON(): string;
}
class User implements Serializable {
toJSON() { return "{}"; }
}O implements garante que uma classe cumpre o contrato de uma interface. Se faltar um método ou propriedade, o compilador avisa. Uma classe pode implementar várias interfaces.
override
class Base {
metodo() { return "base"; }
}
class Filha extends Base {
override metodo() { return "filha"; }
}A palavra-chave override marca explicitamente que um método substitui o do pai. Com a opção noImplicitOverride no tsconfig, é obrigatória e evita erros de renomeação.
Modificadores de acesso
class Conta {
public titular: string;
private saldo: number = 0;
protected id: number = 0;
readonly banco: string = "CGD";
}O public é visível em todo o lado, private só na classe, protected na classe e subclasses e readonly impede reatribuição. Por omissão os membros são public.
Abstract class
abstract class Forma {
abstract area(): number;
descricao() { return "forma"; }
}
// new Forma(); // ERROUma classe abstract não pode ser instanciada diretamente e serve de base. Pode ter métodos abstratos (sem corpo, com abstract) que as subclasses são obrigadas a implementar.
Classe genérica
class Pilha<T> {
private itens: T[] = [];
push(item: T) { this.itens.push(item); }
pop(): T | undefined { return this.itens.pop(); }
}
const p = new Pilha<number>();Uma classe genérica usa <T> após o nome para parametrizar os tipos dos seus membros. O tipo é definido ao instanciar com new Classe<tipo>() ou inferido.
Parameter properties
class Ponto {
constructor(
public x: number,
public y: number
) {}
}
const p = new Ponto(1, 2);Um parameter property declara e atribui um campo diretamente no construtor, juntando um modificador (public, private, readonly) ao parâmetro. Reduz muito o código boilerplate.
Getters e setters
class Circulo {
constructor(private raio: number) {}
get area(): number {
return Math.PI * this.raio ** 2;
}
set novoRaio(r: number) { this.raio = r; }
}Os getters (get) e setters (set) definem acesso controlado a uma propriedade. Acedem-se como campos normais, mas permitem validação ou cálculo por trás.
Herança (extends)
class Animal {
constructor(public nome: string) {}
falar() { return "..."; }
}
class Cao extends Animal {
falar() { return "Au!"; }
}A herança com extends permite que uma classe filha reutilize e substitua métodos do pai. O constructor da filha deve chamar super() se o pai tiver um.
Static
class MathUtils {
static readonly PI = 3.14159;
static dobro(n: number): number {
return n * 2;
}
}
MathUtils.dobro(5);Membros static pertencem à classe e não à instância, acedidos com Classe.membro. Podem ser métodos, campos ou readonly. Úteis para utilitários e constantes.
Generics
Função genérica
function primeiro<T>(arr: T[]): T | undefined {
return arr[0];
}
primeiro([1, 2, 3]); // number
primeiro(["a", "b"]); // stringUma função genérica declara <T> antes dos parâmetros. O tipo é inferido a partir dos argumentos e mantém a relação entre entrada e saída, sem recorrer a any.
Genéricos com default
interface Resposta<T = unknown> {
dados: T;
erro?: string;
}
const r: Resposta<string[]> = { dados: [] };
const s: Resposta = { dados: null };Um parâmetro genérico pode ter um valor padrão com T = tipo. Se não for indicado, usa o default. Útil para tornar tipos opcionais sem perder segurança.
Constraint com keyof
function ordenar<T, K extends keyof T>(
itens: T[], chave: K
): T[] {
return [...itens].sort(
(a, b) => (a[chave] > b[chave] ? 1 : -1)
);
}Combinar K extends keyof T com arrays permite funções seguras que operam sobre uma propriedade específica. O compilador verifica que a chave existe em T.
Interface genérica
interface Caixa<T> {
valor: T;
abrir(): T;
}
const c: Caixa<number> = {
valor: 42,
abrir: () => 42,
};Uma interface genérica usa <T> para criar tipos reutilizáveis. O parâmetro é fornecido ao usar (Caixa<number>). Muito comum em respostas de API e contentores.
Múltiplos parâmetros
function juntar<A, B>(a: A, b: B): A & B {
return { ...a, ...b };
}
const r = juntar({ x: 1 }, { y: 2 });
// r: { x: number } & { y: number }Uma função pode ter vários parâmetros de tipo, separados por vírgulas. Cada um é inferido independentemente. O retorno A & B combina as formas dos dois objetos.
Genéricos em Promises
async function fetchUser(id: number): Promise<User> {
const r = await fetch(`/api/${id}`);
return r.json();
}
const u = await fetchUser(1); // UserO Promise<T> é genérico: indica o tipo do valor resolvido. Ao anotar o retorno de funções async, o await devolve o tipo correto. Essencial para código assíncrono type-safe.
Constraints (extends)
function maior<T extends { length: number }>(
a: T, b: T
): T {
return a.length > b.length ? a : b;
}Uma constraint T extends Tipo restringe o genérico a tipos que tenham certa forma. Aqui garante que T tem length, permitindo aceder a essa propriedade em segurança.
Indexed access (T[K])
interface User {
nome: string;
idade: number;
}
type Nome = User["nome"]; // string
type Valor = User[keyof User]; // string | numberO indexed access Tipo["campo"] extrai o tipo de uma propriedade. Com Tipo[keyof Tipo] obténs o union de todos os valores. É uma forma de derivar tipos sem duplicar.
keyof
function get<T, K extends keyof T>(
obj: T, key: K
): T[K] {
return obj[key];
}
get({ nome: "Ana" }, "nome"); // stringO keyof T produz um union das chaves de T. Combinado com K extends keyof T, garante que a chave existe. O tipo de retorno T[K] é o tipo dessa propriedade.
Arrow genérica em .tsx
// em .ts const id = <T>(x: T): T => x; // em .tsx (React) usa trailing comma const id2 = <T,>(x: T): T => x;
Em ficheiros .tsx, a seta genérica <T> confunde-se com JSX. Adiciona uma vírgula <T,> ou usa <T extends unknown> para o parser reconhecer o genérico.
Utility Types
Partial / Required
type Parcial = Partial<Pessoa>; // todos os campos opcionais type Completo = Required<Parcial>; // todos obrigatórios
O Partial<T> torna todas as propriedades opcionais, útil para funções de atualização. O Required<T> faz o inverso, removendo os ? e tornando tudo obrigatório.
ReturnType
function somar(a: number, b: number) {
return a + b;
}
type R = ReturnType<typeof somar>; // numberO ReturnType<T> extrai o tipo de retorno de uma função. Usa-se com typeof funcao para obter o tipo da função. Evita duplicar o tipo de retorno manualmente.
Awaited
type A = Awaited<Promise<string>>; // string type B = Awaited<Promise<number[]>>; // number[] type C = Awaited<boolean>; // boolean
O Awaited<T> "desembrulha" o tipo resolvido de uma Promise, tal como o operador await. Funciona recursivamente em promises aninhadas. Útil para tipar resultados assíncronos.
Pick / Omit
type Nome = Pick<Pessoa, "nome">; type SemEmail = Omit<Pessoa, "email">; type Basico = Pick<User, "id" | "nome">;
O Pick<T, K> seleciona apenas as propriedades indicadas e o Omit<T, K> exclui-as, mantendo o resto. São ideais para criar variantes de um tipo sem o reescrever.
Extract / Exclude
type T = "a" | "b" | "c"; type AB = Extract<T, "a" | "b">; // "a" | "b" type C = Exclude<T, "a" | "b">; // "c"
O Extract<T, U> mantém de um union só os tipos atribuíveis a U; o Exclude<T, U> remove-os. São úteis para filtrar membros de unions de literais.
InstanceType
class User {
nome = "Ana";
}
type U = InstanceType<typeof User>; // User
type Ctor = new () => User;O InstanceType<T> extrai o tipo da instância de um construtor. Usa-se com typeof Classe. É comum em fábricas genéricas que recebem construtores e devolvem instâncias.
Record
type Notas = Record<string, number>;
const n: Notas = { math: 95, port: 88 };
type Mapa = Record<"a" | "b", boolean>;O Record<K, V> cria um objeto cujas chaves são do tipo K e valores do tipo V. Mais preciso que uma index signature quando as chaves são conhecidas.
NonNullable
type T = string | null | undefined;
type NN = NonNullable<T>; // string
function usar(v: string | undefined) {
const s: NonNullable<typeof v> = v ?? "";
}O NonNullable<T> remove null e undefined de um tipo. É útil para garantir que um valor está presente após uma verificação, sem reescrever o tipo.
Uppercase / Capitalize
type A = Uppercase<"ana">; // "ANA" type B = Lowercase<"ANA">; // "ana" type C = Capitalize<"ana">; // "Ana" type D = Uncapitalize<"Ana">; // "ana"
Os utility types de template literal transformam strings a nível de tipo: Uppercase, Lowercase, Capitalize e Uncapitalize. Só operam em tipos literais de string.
Readonly<T>
type Imutavel = Readonly<Pessoa>;
const p: Imutavel = { nome: "Ana", idade: 30 };
// p.idade = 31; // ERRO: readonlyO Readonly<T> torna todas as propriedades de T imutáveis. Qualquer reatribuição causa erro. É superficial (não afeta objetos aninhados); para arrays usa ReadonlyArray.
Parameters
function somar(a: number, b: number) {
return a + b;
}
type P = Parameters<typeof somar>;
// [a: number, b: number]O Parameters<T> extrai os tipos dos parâmetros de uma função como um tuplo. Usa-se com typeof. Prático para reutilizar a assinatura de funções existentes.
Tipos Avançados
Type guards
function processar(v: string | number) {
if (typeof v === "string") {
return v.toUpperCase();
}
return v.toFixed(2);
}Os type guards usam typeof, instanceof ou verificações para afunilar (narrowing) um union. Dentro de cada ramo o TypeScript sabe o tipo exato.
Template literal types
type Evento = `on${Capitalize<"click" | "focus">}`;
// "onClick" | "onFocus"
type Rota = `/${string}`;Os template literal types criam tipos string a partir de template literals. Combinados com unions geram todas as combinações. Úteis para nomes de eventos, rotas e padrões.
Exhaustiveness (never)
function area(f: Forma): number {
switch (f.tipo) {
case "circulo": return Math.PI * f.raio ** 2;
case "quadrado": return f.lado ** 2;
default:
const _ex: never = f;
return _ex;
}
}Atribuir o caso default a uma variável never garante exaustão: se adicionares um novo caso ao union sem tratar, o compilador avisa. Torna o switch à prova de futuro.
Discriminated unions
type Forma =
| { tipo: "circulo"; raio: number }
| { tipo: "quadrado"; lado: number };
function area(f: Forma) {
switch (f.tipo) {
case "circulo": return Math.PI * f.raio ** 2;
case "quadrado": return f.lado ** 2;
}
}Uma discriminated union é um union de objetos com um campo comum literal (o discriminador, aqui tipo). Um switch sobre ele faz narrowing automático e seguro.
infer
type Unpack<T> = T extends Promise<infer U>
? U : T;
type R = Unpack<Promise<string>>; // stringA palavra-chave infer declara uma variável de tipo dentro de um conditional, deixando o TypeScript deduzi-la. Aqui extrai o tipo interno de uma Promise.
Recursive types
type Json =
| string
| number
| boolean
| null
| Json[]
| { [key: string]: Json };Um recursive type referencia-se a si próprio, descrevendo estruturas aninhadas como JSON ou árvores. O TypeScript resolve a recursão automaticamente. Útil para dados hierárquicos.
Mapped types
type Opcional<T> = {
[K in keyof T]?: T[K];
};
type Nullable<T> = {
[K in keyof T]: T[K] | null;
};Um mapped type cria um novo tipo percorrendo as chaves de outro com [K in keyof T]. Permite transformar todas as propriedades programaticamente. É a base dos utility types.
Narrowing com in
type Cao = { latir(): void };
type Gato = { miar(): void };
function falar(a: Cao | Gato) {
if ("latir" in a) a.latir();
else a.miar();
}O operador in verifica se uma propriedade existe no objeto e faz narrowing do union. É uma alternativa ao typeof quando os tipos se distinguem por campos presentes.
Mapped com modificadores
type SoLeitura<T> = {
+readonly [K in keyof T]: T[K];
};
type SemOpcionais<T> = {
[K in keyof T]-?: T[K];
};Num mapped type, os prefixos + e - adicionam ou removem modificadores. +readonly torna imutável e -? remove a opcionalidade. Dão controlo fino sobre a transformação.
Conditional types
type IsString<T> = T extends string
? "sim" : "não";
type A = IsString<string>; // "sim"
type B = IsString<number>; // "não"Um conditional type escolhe entre dois tipos com a sintaxe T extends U ? X : Y. É o "if" do sistema de tipos. Combina com infer para extrair tipos.
Type predicates (is)
function isString(v: unknown): v is string {
return typeof v === "string";
}
if (isString(valor)) {
valor.toUpperCase(); // string
}Um type predicate param is Tipo no retorno cria um guard personalizado. Quando a função devolve true, o TypeScript afunila o tipo no ramo. Ideal para verificações reutilizáveis.
Módulos e Enums
Export / Import
// modulo.ts
export const PI = 3.14;
export function somar(a: number, b: number) { }
export default class App { }
// uso.ts
import App, { PI, somar } from "./modulo";O export disponibiliza valores e o import traz-os de outro módulo. O export default define a exportação principal, importada sem chavetas. Os nomeados usam { }.
Enum numérico vs string
enum Num { A, B, C } // 0, 1, 2
enum Str { A = "a", B = "b" }
let n: Num = Num.A; // 0
let s: Str = Str.A; // "a"Enums numéricos auto-incrementam a partir de 0 e permitem mapeamento inverso. Enums string são mais legíveis em logs e debugging. Os de string são geralmente mais seguros.
Re-export e barrel
// index.ts (barrel)
export { User } from "./user";
export { Post } from "./post";
export * from "./utils";
import { User, Post } from "./index";Um ficheiro barrel (geralmente index.ts) re-exporta vários módulos com export { } ou export *. Simplifica os imports, permitindo importar tudo de um só local.
Type-only import/export
import type { User } from "./tipos";
export type { Config } from "./config";
import { type Options, criar } from "./lib";O import type importa apenas tipos, que são removidos na compilação. Evita imports desnecessários em runtime e conflitos. Com verbatimModuleSyntax no tsconfig torna-se obrigatório.
Namespace
namespace Utils {
export function format(s: string) {
return s.trim();
}
}
Utils.format("olá");Um namespace agrupa código relacionado sob um nome. É uma forma antiga de organização; hoje os módulos ES (import/export) são preferidos. Ainda útil em código legado ou global.
Enums
enum Direcao {
Cima = "UP",
Baixo = "DOWN",
}
let d: Direcao = Direcao.Cima;Um enum define um conjunto de constantes nomeadas. Pode ter valores string ou numéricos. Acede-se com Enum.Membro. Em código moderno, unions de literais são muitas vezes preferidos.
Declaration files (.d.ts)
// global.d.ts
declare const API_URL: string;
interface Window {
minhaLib: string;
}Ficheiros .d.ts contêm apenas declarações de tipos, sem código executável. Servem para tipar bibliotecas sem tipos ou variáveis globais. O declare anuncia existência sem implementar.
Const enum
const enum Status {
Ativo = 1,
Inativo = 0,
}
let s = Status.Ativo; // inlinado como 1Um const enum é totalmente inlinado em tempo de compilação, sem gerar objeto em runtime. É mais eficiente, mas não pode ser inspecionado dinamicamente. Usa com moderação.
declare module (wildcard)
declare module "*.css" {
const classes: Record<string, string>;
export default classes;
}
declare module "lib-sem-tipos";O declare module com padrão (ex.: "*.css") tipa imports não-TS, como CSS ou imagens. Também declara módulos sem tipos para evitar erros. Coloca-se num .d.ts.
Dicas e Configuração
tsconfig.json essencial
{
"compilerOptions": {
"strict": true,
"target": "ES2022",
"module": "ESNext",
"outDir": "./dist",
"esModuleInterop": true
}
}O tsconfig.json configura o compilador. O strict: true ativa todas as verificações rigorosas. target define a versão do JS gerado e outDir o destino. Cria com tsc --init.
strict e flags úteis
{
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"noUnusedLocals": true,
"noImplicitOverride": true
}O strict ativa strictNullChecks, noImplicitAny e outros. Flags como noUnusedLocals e noImplicitOverride reforçam a qualidade. Ativa o máximo que o projeto permitir.
Erros comuns
// usar == em vez de === // esquecer await numa Promise // any em vez de unknown // mutar um readonly // comparar objeto com ===
Erros frequentes: usar any em vez de unknown, esquecer await, comparar objetos com === (compara referência) e ignorar readonly. O strict apanha a maioria.
CLI: comandos
npx tsc --init // cria tsconfig npx tsc --watch // recompila ao guardar npx tsc --noEmit // só verifica tipos npx tsc app.ts // compila um ficheiro
O compilador tsc compila e verifica tipos. --watch recompila automaticamente e --noEmit só valida sem gerar ficheiros, útil em CI. Instala com npm i -D typescript.
Optional chaining (?.)
const cidade = user?.endereco?.cidade; const metodo = obj.metodo?.(); const item = lista?.[0];
O optional chaining ?. acede a propriedades aninhadas com segurança: se algum valor for null ou undefined, devolve undefined em vez de lançar erro. Funciona em métodos e índices.
Boas práticas
// evitar any (usar unknown) // ativar strict: true // interfaces para objetos // type para unions/aliases // narrowing em vez de as
Evita any (prefere unknown), ativa strict, usa interface para objetos e type para unions. Prefere narrowing a as e deixa o compilador inferir sempre que possível.
Non-null assertion (!)
let el = document.getElementById("app")!;
el.textContent = "Olá";
// diz ao TS: "não é null/undefined"O operador ! (non-null assertion) garante ao compilador que um valor não é null nem undefined. Não faz verificação em runtime; usa-o só quando tens a certeza.
Definite assignment (!)
class User {
nome!: string; // será atribuído depois
constructor() {
this.init();
}
init() { this.nome = "Ana"; }
}O ! após o nome de um campo (definite assignment) diz ao TypeScript que a propriedade será inicializada, mesmo que não no construtor. Evita o erro de "não inicializado". Usa com cuidado.
satisfies (TS 4.9+)
const config = {
host: "localhost",
porta: 3000,
} satisfies Record<string, string | number>;
config.porta.toFixed(); // ainda inferidoO operador satisfies valida que um valor cumpre um tipo sem perder a inferência específica. Ao contrário de : tipo, mantém os tipos exatos das propriedades. Combina segurança e precisão.
@ts-expect-error
// @ts-expect-error: valor inválido propositado const n: number = "texto"; // @ts-ignore: suprime o próximo erro const x: string = 123;
O comentário @ts-expect-error suprime um erro esperado e avisa se ele deixar de existir (melhor que @ts-ignore). Usa-os pontualmente e com justificação, nunca em massa.