TypeScript: O sistema de módulos do TypeScript
Última atualização: 2026-08-26
Os módulos são as unidades básicas para organizar o código — o TypeScript oferece suporte a módulos ES e módulos CommonJS, além de permitir a exportação de tipos.
1. Noções básicas sobre módulos ES
(1) Exportação de nomes
// utils.ts —— Name Export
export function add(a: number, b: number): number {
return a + b;
}
export function multiply(a: number, b: number): number {
return a * b;
}
export const PI = 3.14159;
// main.ts —— Import by Name
import { add, multiply, PI } from "./utils";
console.log(add(1, 2)); // 3
console.log(multiply(3, 4)); // 12
console.log(PI); // 3.14159
(2) Exportação padrão
// logger.ts —— Default Export
export default class Logger {
constructor(private prefix: string) {}
log(message: string): void {
console.log(`[${this.prefix}] ${message}`);
}
}
// main.ts —— Default Import(No curly braces needed,Custom Name)
import Logger from "./logger";
// That's fine, too.:import MyLogger from "./logger";
let logger = new Logger("APP");
logger.log("App Launch"); // [APP] App Launch
(3) Utilizando tanto as exportações padrão quanto as exportações nomeadas
// api.ts
export default class ApiClient {
constructor(private baseUrl: string) {}
async get(path: string): Promise<any> {
// ...
}
}
export enum HttpMethod {
GET = "GET",
POST = "POST",
PUT = "PUT",
DELETE = "DELETE"
}
// main.ts
import ApiClient, { HttpMethod } from "./api";
let client = new ApiClient("https://api.example.com");
let method: HttpMethod = HttpMethod.GET;
▶ Exemplo: Uma calculadora modular
// calculator/operations.ts
export function add(a: number, b: number): number { return a + b; }
export function subtract(a: number, b: number): number { return a - b; }
export function multiply(a: number, b: number): number { return a * b; }
export function divide(a: number, b: number): number {
if (b === 0) throw new Error("The divisor cannot be zero.");
return a / b;
}
Saída:
// Executed successfully
// calculator/index.ts
export { add, subtract, multiply, divide } from "./operations";
export type { Operation } from "./types";
// Default Export——Calculator Category
import * as ops from "./operations";
export default class Calculator {
compute(op: string, a: number, b: number): number {
switch (op) {
case "+": return ops.add(a, b);
case "-": return ops.subtract(a, b);
case "*": return ops.multiply(a, b);
case "/": return ops.divide(a, b);
default: throw new Error(`Unknown Operation:${op}`);
}
}
}
2. Exportação de tipos
O TypeScript permite exportar tipos individualmente — um recurso que não está presente no sistema de módulos do JavaScript:
(1) O modificador type
// types.ts
export interface User {
id: number;
name: string;
email: string;
}
export type UserId = number;
export type UserRole = "admin" | "editor" | "viewer";
// Use export type to explicitly mark"Export Types Only"
export type { User as UserType };
(2) Distinguir entre tipos e valores ao importar
// main.ts
import { type User, type UserRole, createUser } from "./types";
// ↑ type Modifier Notation"Import Types Only"——It will be erased after compilation.
// Equivalent Old Syntax
// import { User, UserRole } from "./types"; // May result in runtime imports
// Recommended New Syntax——Clearly Distinguish Between Type Import and Value Import
import type { User, UserRole } from "./types";
import { createUser } from "./types";
import type Os tipos importados são completamente removidos após a compilação — eles não resultam em chamadas em tempo de execução para require ou import. Isso é fundamental para cenários que utilizam apenas tipos (como anotações de tipo e interfaces), pois evita o carregamento desnecessário de módulos.
(3) Importações de tipos inline
// Mixed Import——Values and Types
import { createUser, type User, type UserRole } from "./types";
// createUser is the value——Requirements for runtime
// User and UserRole is a type——Compile-Time Erasure
3. Reexportação e arquivos de bucket
(1) Reexportação
// Re-exporting members of one module from another module
export { User, UserId } from "./user-types";
export { Product, ProductId } from "./product-types";
export { Order, OrderId } from "./order-types";
(2) Lixa cilíndrica
index.ts Como ponto de entrada para o diretório, reexporte todas as APIs públicas:
// models/index.ts —— Bucket files
export { User, UserId } from "./user";
export { Product, ProductId } from "./product";
export { Order, OrderId } from "./order";
export type { CreateUser, UpdateUser } from "./user";
export type { CreateProduct, UpdateProduct } from "./product";
// Import directly from the directory when using it
import { User, Product, type CreateUser } from "./models";
// without needing to know which specific file it is in
4. Estratégias de análise de módulos
O TypeScript precisa saber como resolver import "./utils" em um arquivo real — isso é determinado pela estratégia de resolução de módulos.
(1) Duas estratégias de análise sintática
| Estratégia | Objetivo | Descrição |
|---|---|---|
classic |
Compatibilidade com versões anteriores | Pesquise primeiro por .ts e, em seguida, por .d.ts |
node (Recomendado) |
Projeto TS moderno | Simula a lógica de análise do Node.js |
(2) Ordem de consulta para estratégias de análise de nós
import { X } from "./utils"
Ordem de pesquisa:
./utils.ts./utils.tsx./utils.d.ts- O campo
typesem./utils/package.json ./utils/index.ts./utils/index.d.ts
(3) Pesquisando em node_modules
import _ from "lodash"
Ordem de pesquisa:
./node_modules/lodash.ts(Não existe)- Campo
./node_modules/lodash/package.json→types/typings ./node_modules/lodash/index.d.ts./node_modules/@types/lodash/index.d.ts- Pesquise
../node_modules/→../../node_modules/...
5. Mapeamento de caminhos (Path Mapping)
Use aliases de caminho em projetos grandes para evitar caminhos relativos longos:
(1) Configuração do tsconfig.json
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"],
"@utils/*": ["src/utils/*"],
"@models/*": ["src/models/*"],
"@components/*": ["src/components/*"]
}
}
}
(2) Uso de aliases de caminho
// No aliases——Relative paths are prone to errors
import { User } from "../../../models/user";
import { formatDate } from "../../utils/date";
// Has an alias——Clear and concise
import { User } from "@models/user";
import { formatDate } from "@utils/date";
@models. É necessário usar uma ferramenta de compilação (como o resolve.alias do Webpack, o resolve.alias do Vite ou o tsc-alias) para realizar a substituição de caminhos em tempo de execução.
6. Interoperabilidade com CommonJS
(1) Módulos CommonJS
// Use CommonJS style export
// math.cjs
const add = (a, b) => a + b;
const multiply = (a, b) => a * b;
module.exports = { add, multiply };
(2) Importando CommonJS no TS
// esModuleInterop: false(Default)
import * as math from "./math.cjs";
math.add(1, 2);
// esModuleInterop: true(Recommendations)
import math from "./math.cjs"; // ✅ A More Natural Way to Introduce It
math.add(1, 2);
(3) Permitir exportações CommonJS a partir do TS
// Use export = syntax to export CommonJS Style
class Calculator {
add(a: number, b: number): number { return a + b; }
}
export = Calculator;
// Use the following when importing: import = require
import Calculator = require("./calculator");
let calc = new Calculator();
import/export) em todos os novos projetos e habilite esModuleInterop: true para manter a compatibilidade com pacotes CommonJS legados. Use export = e import = apenas quando for necessária compatibilidade estrita com o CommonJS.
▶ Exemplo: Exportações nomeadas vs padrão lado a lado
// config.ts — named and default exports together
export const APP_NAME = "MyApp";
export const VERSION = "1.0.0";
export default class AppConfig {
constructor(public port: number = 3000) {}
toString(): string { return `${APP_NAME} v${VERSION} on :${this.port}`; }
}
// main.ts — importing both styles
import AppConfig, { APP_NAME, VERSION } from "./config";
let config = new AppConfig(8080);
console.log(config.toString()); // MyApp v1.0.0 on :8080
console.log(APP_NAME); // MyApp
Saída:
MyApp v1.0.0 on :8080
MyApp
▶ Exemplo: import type para importações apenas de tipo
// shapes.ts
export interface Circle { kind: "circle"; radius: number; }
export interface Square { kind: "square"; side: number; }
export type Shape = Circle | Square;
export function area(shape: Shape): number {
return shape.kind === "circle"
? Math.PI * shape.radius ** 2
: shape.side ** 2;
}
// main.ts — import type for types, regular import for values
import type { Circle, Square, Shape } from "./shapes";
import { area } from "./shapes";
let c: Circle = { kind: "circle", radius: 5 };
let s: Shape = { kind: "square", side: 4 };
console.log(area(c)); // 78.5398...
console.log(area(s)); // 16
Saída:
78.53981633974483
16
❓ Perguntas Frequentes
P: Qual é a diferença entre
import typee umimportcomum? R:import typeOs tipos importados dessa forma são completamente apagados após a compilação e não resultam no carregamento de módulos em tempo de execução. Uma instruçãoimportcomum importa tanto valores quanto tipos e resulta em uma chamadarequire()após a compilação. Ao usar apenas tipos (como interfaces ou aliases de tipo), certifique-se de usarimport typepara evitar importações desnecessárias em tempo de execução.
P: Deve-se usar arquivos de agrupamento (index.ts)? R: Eles são recomendados para bibliotecas e APIs públicas — para simplificar as importações e controlar as interfaces exportadas. Para aplicativos internos, depende da situação — eles não são necessários para projetos pequenos, mas oferecem benefícios organizacionais para projetos grandes. A principal desvantagem é que eles podem afetar o “tree-shaking”, mas os empacotadores modernos lidam bem com isso.
P: Como os aliases de caminho entram em vigor em tempo de execução? R: O código JS gerado pelo TypeScript ainda usa caminhos de alias (como
@models/user), que não são reconhecidos em tempo de execução. A substituição de caminhos deve ser feita por uma ferramenta de compilação — o Webpack usaresolve.alias, o Vite usaresolve.aliase a compilação pura com o tsc requer pós-processamento por ferramentas comotsc-alias.
P: O que devo usar, ES Modules ou CommonJS? R: Para novos projetos, use exclusivamente ES Modules (
import/export). O CommonJS é o sistema de módulos legado do Node.js e está sendo descontinuado. A opçãoesModuleInteropno TypeScript permite que você use pacotes CommonJS sem problemas. Os navegadores e o Deno suportam apenas ES Modules.
📖 Resumo
- Os módulos ES utilizam
export/importcomo códigos de organização; as chaves são usadas para exportações nomeadas, mas não para exportações padrão. import typeApenas tipos de importação — apagar após a compilação; não ocorre carregamento de módulos em tempo de execução- O arquivo
index.tsreexporta as APIs públicas contidas no diretório para simplificar o caminho de importação - Estratégia de análise de módulos
node(Recomendada) Simula a lógica de pesquisa do Node.js - O mapeamento de caminhos (
baseUrl+paths) substitui caminhos relativos longos por aliases; requer uma ferramenta de compilação que ofereça suporte a isso esModuleInterop: trueHabilitando a interoperabilidade perfeita entre módulos ES e CommonJS
📝 Exercícios
- Exercício básico (Dificuldade ⭐): Crie três arquivos de módulo —
math.ts(exporta soma/subtração),string-utils.ts(exporta maiúsculas/inversão) eindex.ts(reexporta o arquivo bucket). Importe-os e utilize-os nomain.ts. - Exercício avançado (Dificuldade ⭐⭐): Configure os mapeamentos de caminho para um projeto existente — mapeie
@utilsparasrc/utilse@modelsparasrc/models. Useimport typepara importar tipos e use simplesmenteimportpara importar valores. - Desafio (Dificuldade: ⭐⭐⭐): Escreva um arquivo de declaração que permita que o pacote CommonJS
legacy-sdkseja importado para o TypeScript no estilo ES Module —import LegacySDK from "legacy-sdk". Considere os dois casos: quandoesModuleInteropestá habilitado e quando está desabilitado.