🔷Linguagem

Guia Completo de TypeScript

Do tsconfig aos padrões avançados — generics, utility types, narrowing, mapped types, satisfies.

Progresso
0%

Configuração e tsconfig

O tsconfig.json controla como o compilador TypeScript transpila seu código. Uma boa configuração previne bugs e habilita as melhores features.

// tsconfig.json — configuração recomendada para Node.js moderno
{
  "compilerOptions": {
    "target": "ES2022",          // features modernas de JS
    "module": "NodeNext",        // ESM nativo no Node
    "moduleResolution": "NodeNext",
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,              // SEMPRE true — habilita todas as checks
    "noUncheckedIndexedAccess": true, // array[0] é T | undefined
    "exactOptionalPropertyTypes": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,
    "paths": { "@/*": ["./src/*"] }, // alias de importação
    "skipLibCheck": true,
    "declaration": true,
    "declarationMap": true,
    "sourceMap": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}
strict: true habilita: strictNullChecks, strictFunctionTypes, noImplicitAny, strictPropertyInitialization e mais. Nunca desative — encontra bugs reais.

Tipos Básicos e Primitivos

// Primitivos
let name: string = 'Kaique';
let age: number = 28;
let active: boolean = true;
let data: unknown = fetchData(); // use unknown, não any
let id: string | number = getId(); // union type

// Arrays
const nums: number[] = [1, 2, 3];
const strs: Array<string> = ['a', 'b']; // equivalente
const tuple: [string, number] = ['nome', 42]; // tamanho e tipos fixos

// Object types inline
const user: { name: string; email: string; age?: number } = {
  name: 'Kaique', email: '[email protected]'
};

// Literal types
type Direction = 'north' | 'south' | 'east' | 'west';
type Status = 200 | 201 | 400 | 404 | 500;

// null e undefined com strictNullChecks
function find(id: string): User | null { ... }
const user = find('123');
if (user !== null) {
  console.log(user.name); // TypeScript sabe que não é null aqui
}

// never — tipo de funções que nunca retornam
function throwError(msg: string): never {
  throw new Error(msg);
}

// void — funções sem retorno significativo
function log(msg: string): void {
  console.log(msg);
}

Interfaces vs Type Aliases

Ambos descrevem formatos de objetos — a escolha entre eles é questão de convenção, mas há diferenças importantes.

// Interface — abre para extensão, melhor para objetos públicos de API
interface User {
  id: string;
  name: string;
  email: string;
}

// Herança de interface
interface AdminUser extends User {
  role: 'admin';
  permissions: string[];
}

// Type alias — mais poderoso, permite union/intersection/mapped types
type ID = string | number;
type NullableUser = User | null;
type UserWithTimestamps = User & { createdAt: Date; updatedAt: Date };

// Quando usar cada um:
// Interface: shapes de objetos, especialmente em APIs públicas e classes
// Type: unions, intersections, utility types, primitives, anything complex

// Diferença crucial: interfaces são abertas (mergeáveis)
interface Config { host: string }
interface Config { port: number } // válido! merge automático
// → Config agora tem { host, port }

Generics

Generics permitem escrever código reutilizável que funciona com múltiplos tipos sem perder type safety.

// Generic básico
function first<T>(arr: T[]): T | undefined {
  return arr[0];
}
const n = first([1, 2, 3]);     // TypeScript infere: T = number
const s = first(['a', 'b']);    // T = string

// Generic com constraint
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}
const user = { name: 'Kaique', age: 28 };
const name = getProperty(user, 'name');  // string
const age  = getProperty(user, 'age');   // number
// getProperty(user, 'foo')  // ❌ Erro! 'foo' não existe em user

// Generic em classes
class Stack<T> {
  private items: T[] = [];
  push(item: T) { this.items.push(item); }
  pop(): T | undefined { return this.items.pop(); }
  peek(): T | undefined { return this.items[this.items.length - 1]; }
}

// Generic em interfaces — repositório genérico
interface Repository<T, ID = string> {
  findById(id: ID): Promise<T | null>;
  findAll(): Promise<T[]>;
  create(data: Omit<T, 'id'>): Promise<T>;
  update(id: ID, data: Partial<T>): Promise<T | null>;
  delete(id: ID): Promise<boolean>;
}

// API Result pattern
type Result<T, E = Error> =
  | { ok: true;  value: T }
  | { ok: false; error: E };

async function safeAsync<T>(fn: () => Promise<T>): Promise<Result<T>> {
  try {
    return { ok: true, value: await fn() };
  } catch (e) {
    return { ok: false, error: e as Error };
  }
}

Utility Types

TypeScript inclui utility types poderosos que transformam tipos existentes sem escrever código duplicado.

interface User {
  id: string;
  name: string;
  email: string;
  password: string;
  createdAt: Date;
}

// Partial<T> — todos os campos opcionais
type UserUpdate = Partial<User>;
// { id?: string; name?: string; ... }

// Required<T> — todos os campos obrigatórios
type StrictUser = Required<User>;

// Pick<T, K> — selecionar campos
type UserPublic = Pick<User, 'id' | 'name' | 'email'>;

// Omit<T, K> — excluir campos
type UserCreate = Omit<User, 'id' | 'createdAt'>;
type UserWithoutPassword = Omit<User, 'password'>;

// Readonly<T> — imutável
type ImmutableUser = Readonly<User>;

// Record<K, V> — dicionário tipado
type UserMap = Record<string, User>;
type HttpStatusMap = Record<200 | 201 | 400 | 404, string>;

// ReturnType<T> — tipo de retorno de função
async function getUsers(): Promise<User[]> { ... }
type UsersResult = Awaited<ReturnType<typeof getUsers>>; // User[]

// Parameters<T> — parâmetros de função
function createUser(name: string, email: string): User { ... }
type CreateParams = Parameters<typeof createUser>; // [string, string]

// Extract e Exclude
type Status = 'pending' | 'active' | 'blocked' | 'deleted';
type ActiveStatuses = Extract<Status, 'pending' | 'active'>; // 'pending' | 'active'
type EditableStatuses = Exclude<Status, 'deleted'>; // 'pending' | 'active' | 'blocked'

// NonNullable<T>
type MaybeUser = User | null | undefined;
type DefiniteUser = NonNullable<MaybeUser>; // User

Type Guards e Narrowing

Type narrowing permite refinar tipos dentro de blocos condicionais, tornando código type-safe com tipos dinâmicos.

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

// instanceof guard
function processError(e: unknown): string {
  if (e instanceof Error) return e.message;
  if (typeof e === 'string') return e;
  return 'Unknown error';
}

// Discriminated union — padrão poderoso para modelagem
type Shape =
  | { kind: 'circle';    radius: number }
  | { kind: 'square';    side: number }
  | { kind: 'rectangle'; width: number; height: number };

function area(shape: Shape): number {
  switch (shape.kind) {
    case 'circle':    return Math.PI * shape.radius ** 2;
    case 'square':    return shape.side ** 2;
    case 'rectangle': return shape.width * shape.height;
    // TypeScript garante exhaustividade — se faltar um case, erro de compilação
  }
}

// Custom type guard
interface Cat { meow(): void }
interface Dog { bark(): void }
type Animal = Cat | Dog;

function isCat(animal: Animal): animal is Cat {
  return 'meow' in animal;
}

function makeSound(animal: Animal) {
  if (isCat(animal)) animal.meow();  // TypeScript sabe que é Cat
  else               animal.bark();  // e aqui é Dog
}

// Assertion functions — narrowing via exceção
function assertNonNull<T>(val: T | null | undefined, msg?: string): asserts val is T {
  if (val == null) throw new Error(msg ?? 'Expected non-null value');
}

const user = getUser(); // User | null
assertNonNull(user, 'User not found');
user.name; // ✅ TypeScript sabe que user é User agora

Tipos Avançados e Mapped Types

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

// Mapped types — transformar todas as propriedades
type Getters<T> = {
  [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};

// Conditional types
type IsArray<T> = T extends any[] ? true : false;
type Flatten<T> = T extends Array<infer Item> ? Item : T;

type FlatNum = Flatten<number[]>; // number
type FlatStr = Flatten<string>;   // string (não é array)

// infer — extrair tipo de dentro de outro
type UnwrapPromise<T> = T extends Promise<infer U> ? U : T;
type PromiseResult = UnwrapPromise<Promise<string>>; // string

// Decorators (TypeScript 5+) — requer "experimentalDecorators": true
function log(target: any, key: string, descriptor: PropertyDescriptor) {
  const original = descriptor.value;
  descriptor.value = function (...args: any[]) {
    console.log(`Calling ${key} with`, args);
    return original.apply(this, args);
  };
  return descriptor;
}

class UserService {
  @log
  async createUser(data: CreateUserDto) { ... }
}

Integração com Node.js e Ecossistema

// Tipos para Node.js
import type { IncomingMessage, ServerResponse } from 'http';
import type { Request, Response } from 'express'; // @types/express

// Variáveis de ambiente tipadas com Zod
import { z } from 'zod';

const Env = z.object({
  PORT: z.coerce.number().default(3000),
  DATABASE_URL: z.string(),
  NODE_ENV: z.enum(['development', 'production', 'test'])
});

type Env = z.infer<typeof Env>; // inferir tipo do schema Zod
const env = Env.parse(process.env);

// Paths alias em runtime com tsx/tsup
// tsconfig paths + plugin para resolver em runtime
import { myUtil } from '@/lib/utils'; // @/* → src/*

// Módulo Declaration Merging — estender tipos de libs
declare module 'express' {
  interface Request {
    user?: User;
    requestId?: string;
  }
}

// Satisfies operator (TS 4.9+) — valida sem widening
const config = {
  host: 'localhost',
  port: 5432,
} satisfies Partial<DatabaseConfig>;

// config.port é number (não DatabaseConfig['port'])

// Build com tsup
// tsup.config.ts
import { defineConfig } from 'tsup';
export default defineConfig({
  entry: ['src/index.ts'],
  format: ['cjs', 'esm'],
  dts: true,
  sourcemap: true,
  clean: true,
  splitting: false,
});

Ferramentas do Ecossistema

tsx

Executa .ts diretamente com Node.js. Ótimo para scripts e desenvolvimento.

tsup

Bundler zero-config para bibliotecas TS. Usa esbuild internamente.

Zod

Schema validation + inferência de tipos. Valida dados externos em runtime.

ts-reset

Corrige tipos estranhos do TypeScript (Array.filter, JSON.parse, etc.).

ts-reset: import '@total-typescript/ts-reset' muda JSON.parse de any para unknown e .filter(Boolean) de (T|null)[] para T[]. Pequeno import, grande ganho de segurança.
Node.js← Todos os treinamentos