Guides
Fundamentos ▾
Versionamento ▾
Deploy ▾

1 Setup

npm install -D typescript tsx @types/node
npx tsc --init
// tsconfig.json
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "declaration": true,
    "sourceMap": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}
// package.json
"scripts": {
  "dev":   "tsx watch src/index.ts",
  "build": "tsc",
  "start": "node dist/index.js"
}

2 Tipos Primitivos

let nome: string = 'Alfredo';
let idade: number = 30;
let ativo: boolean = true;
let nulo: null = null;
let indefinido: undefined = undefined;

// any — desativa checagem (evitar)
let qualquer: any = 'texto';

// unknown — any seguro, força checagem antes de usar
let entrada: unknown = JSON.parse(dados);
if (typeof entrada === 'string') entrada.toUpperCase();

// never — função que nunca retorna
function erro(msg: string): never { throw new Error(msg); }

// literal types
let status: 'ativo' | 'inativo' | 'pendente' = 'ativo';
let codigo: 200 | 201 | 400 | 404 | 500 = 200;

// tuple
let par: [string, number] = ['Alfredo', 30];
let rgb: [number, number, number] = [255, 128, 0];

3 Arrays e Objetos

const numeros: number[] = [1, 2, 3];
const nomes: Array<string> = ['a', 'b'];
const imutavel: ReadonlyArray<number> = [1, 2, 3];

// objeto inline
const usuario: { nome: string; idade: number; email?: string } = { nome: 'Alfredo', idade: 30 };

// type alias
type Coordenada = { x: number; y: number };
type ID = string | number;

// assertion
const input = document.getElementById('campo') as HTMLInputElement;

4 Interface vs Type

// Interface — para objetos e herança
interface Usuario {
  id: number;
  nome: string;
  email: string;
}
interface Admin extends Usuario { permissoes: string[]; }

// Type — para unions, intersections e aliases
type Status = 'ativo' | 'inativo';
type ID = string | number;
type Resposta<T> = { data: T; erro: null } | { data: null; erro: string };

// Intersection
type AdminUsuario = Usuario & { permissoes: string[] };

// Regra: interface para APIs públicas, type para unions/generics

5 Funções

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

// opcional e default
function saudar(nome: string, prefixo?: string, pontos: number = 1): string {
  return `${prefixo ?? 'Olá'}, ${nome}${'!'.repeat(pontos)}`;
}

// rest
function juntar(sep: string, ...partes: string[]): string { return partes.join(sep); }

// tipo de função
type Handler = (req: Request, res: Response) => void;

// overloads
function processar(entrada: string): string;
function processar(entrada: number): number;
function processar(entrada: string | number): string | number {
  if (typeof entrada === 'string') return entrada.trim();
  return entrada * 2;
}

6 Generics

// função genérica
function primeiroItem<T>(arr: T[]): T | undefined { return arr[0]; }

// constraint
function tamanho<T extends { length: number }>(item: T): number { return item.length; }

// interface genérica
interface Repositorio<T> {
  buscarPorId(id: number): Promise<T | null>;
  listar(): Promise<T[]>;
  criar(dados: Omit<T, 'id'>): Promise<T>;
  atualizar(id: number, dados: Partial<T>): Promise<T>;
  deletar(id: number): Promise<void>;
}

// classe genérica
class Stack<T> {
  private items: T[] = [];
  push(item: T): void { this.items.push(item); }
  pop(): T | undefined { return this.items.pop(); }
  get tamanho(): number { return this.items.length; }
}

7 Utility Types

interface Produto { id: number; nome: string; preco: number; estoque: number; }

Partial<Produto>           // todos opcionais — útil para PATCH
Required<Produto>          // todos obrigatórios
Pick<Produto, 'id'|'nome'> // seleciona campos
Omit<Produto, 'id'>        // exclui campos — útil para CREATE
Readonly<Produto>          // imutável

// Record — dicionário
type EstoquePorCategoria = Record<string, number>;
type CategoriaId = 'eletronicos' | 'roupas';
type MapaCategorias = Record<CategoriaId, Produto[]>;

// ReturnType / Parameters
function buscar() { return { id: 1, nome: 'x' }; }
type ResultadoBusca = ReturnType<typeof buscar>;  // { id: number; nome: string }

// NonNullable — remove null e undefined
type SemNull = NonNullable<string | null | undefined>;  // string

// Extract / Exclude
type NumOuStr = string | number | boolean;
type ApenasNum = Extract<NumOuStr, number>;   // number
type SemBool  = Exclude<NumOuStr, boolean>;   // string | number

8 Narrowing / Type Guards

// typeof guard
function processar(valor: string | number) {
  if (typeof valor === 'string') return valor.toUpperCase();
  return valor.toFixed(2);
}

// instanceof
function formatar(erro: Error | string) {
  if (erro instanceof Error) return erro.message;
  return erro;
}

// discriminated union — mais robusto
type Resultado<T> =
  | { ok: true;  dados: T }
  | { ok: false; erro: string };

function tratar<T>(r: Resultado<T>) {
  if (r.ok) console.log(r.dados);
  else      console.error(r.erro);
}

// type predicate
function ehUsuario(obj: unknown): obj is Usuario {
  return typeof obj === 'object' && obj !== null && 'id' in obj;
}

// assertion function
function garantir<T>(v: T | null | undefined, msg: string): asserts v is T {
  if (v == null) throw new Error(msg);
}

9 Classes

class Servico {
  readonly id: string;
  private _ativo: boolean = true;
  protected nome: string;

  constructor(nome: string) {
    this.id = crypto.randomUUID();
    this.nome = nome;
  }

  get ativo(): boolean { return this._ativo; }
  desativar(): void { this._ativo = false; }
  static criar(nome: string) { return new Servico(nome); }
}

// shorthand — auto-assign no constructor
class Produto {
  constructor(
    public readonly id: number,
    public nome: string,
    private preco: number,
  ) {}
}

// implementar interface
class ServicoComLog extends Servico implements Logavel {
  log(msg: string): void { console.log(`[${this.nome}] ${msg}`); }
}

10 TypeScript + Node.js

npm install -D @types/node
// src/index.ts
import http from 'node:http';

const porta: number = Number(process.env.PORT) || 3000;

const servidor = http.createServer((req, res) => {
  res.writeHead(200, { 'Content-Type': 'application/json' });
  res.end(JSON.stringify({ ok: true }));
});

servidor.listen(porta, () => console.log(`Porta ${porta}`));
# dev sem compilar
npx tsx src/index.ts
npx tsx watch src/index.ts   # watch mode

# produção
npx tsc && node dist/index.js

11 TypeScript + Express

npm install express
npm install -D @types/express
import { Router, Request, Response, NextFunction } from 'express';

interface CriarUsuarioBody { nome: string; email: string; senha: string; }

// req body tipado
router.post('/', async (
  req: Request<{}, {}, CriarUsuarioBody>,
  res: Response,
  next: NextFunction
) => {
  const { nome, email } = req.body;
  res.status(201).json({ nome, email });
});

// params tipados
router.get('/:id', (req: Request<{ id: string }>, res: Response) => {
  const id = Number(req.params.id);
  res.json({ id });
});
// tipos/index.ts — estender Request
import { Request } from 'express';

export interface ReqAutenticado extends Request {
  usuario: { id: number; role: 'admin' | 'user' };
}

// middleware auth
export function autenticar(req: Request, res: Response, next: NextFunction): void {
  const token = req.headers.authorization?.replace('Bearer ', '');
  if (!token) { res.status(401).json({ erro: 'Token obrigatório' }); return; }
  (req as ReqAutenticado).usuario = { id: 1, role: 'admin' };
  next();
}

12 TypeScript + React

npm create vite@latest meu-app -- --template react-ts
// props tipadas
interface Props {
  titulo: string;
  descricao?: string;
  onClicar: (id: number) => void;
  children?: React.ReactNode;
}

const Card: React.FC<Props> = ({ titulo, onClicar, children }) => (
  <div onClick={() => onClicar(1)}>{titulo}{children}</div>
);

// hooks tipados
const [contador, setContador] = React.useState<number>(0);
const [usuario, setUsuario] = React.useState<Usuario | null>(null);
const inputRef = React.useRef<HTMLInputElement>(null);

// eventos
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => console.log(e.target.value);
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => e.preventDefault();

// custom hook
function useFetch<T>(url: string) {
  const [dados, setDados] = React.useState<T | null>(null);
  const [carregando, setCarregando] = React.useState(true);
  React.useEffect(() => {
    fetch(url).then(r => r.json() as Promise<T>).then(setDados).finally(() => setCarregando(false));
  }, [url]);
  return { dados, carregando };
}

13 Enums

// enum string — legível no runtime
enum Status {
  Ativo   = 'ATIVO',
  Inativo = 'INATIVO',
  Pendente = 'PENDENTE',
}

// const enum — removido no JS gerado
const enum Tamanho { P = 'P', M = 'M', G = 'G' }

// alternativa moderna — union de literais (sem overhead)
type StatusUsuario = 'ATIVO' | 'INATIVO' | 'PENDENTE';
const STATUS = { ATIVO: 'ATIVO', INATIVO: 'INATIVO' } as const;
type StatusType = typeof STATUS[keyof typeof STATUS];

14 Tipos Avançados

// Mapped types
type Flags<T> = { [K in keyof T]: boolean };

// Conditional types
type SemArray<T> = T extends Array<infer U> ? U : T;
type Res = SemArray<string[]>;   // string

// Template literal types
type Evento = 'click' | 'focus' | 'blur';
type HandlerNome = `on${Capitalize<Evento>}`;
// 'onClick' | 'onFocus' | 'onBlur'

// Infer
type RetornoPromise<T> = T extends Promise<infer U> ? U : T;
type Res2 = RetornoPromise<Promise<string>>;  // string

// keyof + typeof
const config = { host: 'localhost', porta: 3000 };
type ConfigKey = keyof typeof config;             // 'host' | 'porta'

15 Boas Práticas

RegraMotivo
Habilitar strict: truePega erros reais que modo laxo ignora
Evitar anyDesativa tipagem — usar unknown
Preferir union de literais a enumsSem overhead no runtime JS
Discriminated unions para errosMais seguro que exceptions
Validar env vars na inicializaçãoFalha rápido, não em runtime
// Padrão Result — sem exceptions
type Result<T, E = Error> =
  | { ok: true;  valor: T }
  | { ok: false; erro: E };

async function buscar(id: number): Promise<Result<Usuario>> {
  try {
    const u = await db.find(id);
    if (!u) return { ok: false, erro: new Error('Não encontrado') };
    return { ok: true, valor: u };
  } catch (e) {
    return { ok: false, erro: e as Error };
  }
}

// Validar env vars
function envVar(nome: string): string {
  const v = process.env[nome];
  if (!v) throw new Error(`Env var ${nome} não definida`);
  return v;
}
const config = { porta: Number(envVar('PORT')), dbUrl: envVar('DATABASE_URL') } as const;