kb.erickguedes.com
TypeScript: Tipagem e Produtividade

Módulos, Declarações e Decorators

Aula 5 de 7

ES Modules com TypeScript

// @/utils/matematica.ts
export function somar(a: number, b: number): number {
  return a + b;
}

export const PI = 3.14159;

export type Operacao = "soma" | "subtracao" | "multiplicacao";

// @/main.ts
import { somar, PI, type Operacao } from "./utils/matematica.js";

const op: Operacao = "soma";
console.log(somar(2, PI));

Import/Export de Tipos

// Re-export com tipo
export { somar, type Operacao } from "./utils/matematica.js";

// Import type puro (só em tempo de compilação)
import type { Usuario } from "./types.js";

Ambient Declarations (.d.ts)

Arquivos .d.ts declaram tipos sem implementação:

// @/types/global.d.ts
declare const VERSION: string;

declare function log(...args: unknown[]): void;

declare namespace MeuApp {
  interface Config {
    debug: boolean;
    apiUrl: string;
  }
}

declare module

Declara tipos para módulos sem tipos próprios:

// @/types/modules.d.ts
declare module "*.module.css" {
  const classes: Record<string, string>;
  export default classes;
}

declare module "*.svg" {
  const src: string;
  export default src;
}

declare module "minha-biblioteca" {
  export function fazerAlgo(param: string): void;
  export const versao: string;
}

Declaration Merging

TypeScript mescla declarações com o mesmo nome:

// @/express.d.ts
import "express";

declare module "express" {
  interface Request {
    usuario?: {
      id: number;
      nome: string;
    };
  }
}

// Agora Request tem usuario disponível globalmente
// app.get("/", (req, res) => {
//   console.log(req.usuario?.nome);
// });

Triple-Slash Directives

Diretivas de referência para arquivos de declaração:

/// <reference types="vite/client" />
/// <reference path="./types/global.d.ts" />
/// <reference lib="es2022" />

Usado principalmente em .d.ts para referenciar outros arquivos de declaração.

Decorators

Decorators são funções que modificam classes, métodos, propriedades ou parâmetros.

Configuração necessária:

{
  "compilerOptions": {
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true
  }
}

Class Decorator

function selavel<T extends { new (...args: unknown[]): object }>(
  construtor: T
) {
  return class extends construtor {
    criadoEm = new Date();
  };
}

@selavel
class Documento {
  constructor(public titulo: string) {}
}

const doc = new Documento("Relatório");
console.log((doc as any).criadoEm); // Date

Method Decorator

function logAlvo(
  alvo: unknown,
  nomePropriedade: string,
  descritor: PropertyDescriptor
) {
  const metodoOriginal = descritor.value;

  descritor.value = function (...args: unknown[]) {
    console.log(`Chamando ${nomePropriedade} com:`, args);
    const resultado = metodoOriginal.apply(this, args);
    console.log(`Resultado:`, resultado);
    return resultado;
  };
}

class Calculadora {
  @logAlvo
  somar(a: number, b: number): number {
    return a + b;
  }
}

Property Decorator

function dominioValido(alvo: unknown, nomePropriedade: string) {
  let valor: string;

  const getter = () => valor;
  const setter = (novoValor: string) => {
    if (!novoValor.includes("@")) {
      throw new Error("Email inválido");
    }
    valor = novoValor;
  };

  Object.defineProperty(alvo, nomePropriedade, {
    get: getter,
    set: setter,
  });
}

class Usuario {
  @dominioValido
  email = "";
}

Parameter Decorator

import "reflect-metadata";

function validarParametro(alvo: unknown, nomeMetodo: string, indiceParametro: number) {
  Reflect.defineMetadata(
    `validar_${nomeMetodo}`,
    indiceParametro,
    alvo,
    nomeMetodo
  );
}

class Servico {
  processar(@validarParametro id: number) {
    console.log(`Processando ${id}`);
  }
}

Lab: API com Decorators

function Get(rota: string) {
  return function (
    alvo: unknown,
    nomePropriedade: string,
    descritor: PropertyDescriptor
  ) {
    Reflect.defineMetadata("rota", rota, descritor.value!);
    Reflect.defineMetadata("metodo", "GET", descritor.value!);
  };
}

class UserController {
  @Get("/users")
  listar() {
    return [{ id: 1, nome: "Alice" }];
  }
}

// Simulando um framework
function registrarRotas(instancia: Record<string, unknown>) {
  for (const metodo of Object.getOwnPropertyNames(
    Object.getPrototypeOf(instancia)
  )) {
    const fn = (instancia as any)[metodo];
    const rota = Reflect.getMetadata("rota", fn);
    const metodoHttp = Reflect.getMetadata("metodo", fn);

    if (rota) {
      console.log(`Registrando ${metodoHttp} ${rota}`);
    }
  }
}

registrarRotas(new UserController());
# Para usar decorators, instale reflect-metadata
npm install reflect-metadata
npx tsc --noEmit

Decorators são um recurso experimental mas amplamente usado (Angular, NestJS, TypeORM). Para projetos novos, considere se são realmente necessários.