Paths e aliases no tsconfig
1. Introdução aos Paths no TypeScript
Todo desenvolvedor TypeScript já enfrentou o problema dos imports relativos longos e frágeis. Caminhos como ../../../utils/formatDate ou ../../../../services/api são comuns em projetos com estrutura de diretórios profunda. Esses imports não apenas poluem o código visualmente, mas também quebram facilmente quando movemos arquivos de lugar.
Os aliases no TypeScript resolvem esse problema permitindo mapear caminhos curtos e semânticos para diretórios específicos do projeto. Através da propriedade paths no tsconfig.json, podemos definir atalhos como @utils/formatDate ou @services/api, tornando o código mais limpo, seguro e fácil de manter.
2. Configuração básica de paths
A configuração de paths no tsconfig.json segue uma estrutura simples: cada chave representa o alias desejado, e o valor é um array de caminhos reais que o TypeScript deve usar para resolver esse alias.
// tsconfig.json
{
"compilerOptions": {
"baseUrl": ".", // Diretório raiz para resolução
"paths": {
"@/*": ["./src/*"]
}
}
}
O curinga * é essencial nessa configuração. Ele captura qualquer subcaminho após o alias e o substitui no padrão de destino. Por exemplo, @/utils/date será resolvido para ./src/utils/date.
A combinação com baseUrl define o ponto de partida para todos os caminhos em paths. Sem o baseUrl, os caminhos seriam relativos ao local do tsconfig.json, o que pode causar confusão em projetos com múltiplos arquivos de configuração.
// Agora podemos importar assim:
import { formatDate } from '@/utils/date';
import { api } from '@/services/api';
// Em vez de:
import { formatDate } from '../../../utils/date';
import { api } from '../../services/api';
3. Múltiplos aliases e diretórios
Projetos maiores se beneficiam de aliases específicos para cada domínio da aplicação:
// tsconfig.json
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@components/*": ["./src/components/*"],
"@utils/*": ["./src/utils/*"],
"@services/*": ["./src/services/*"],
"@hooks/*": ["./src/hooks/*"],
"@types/*": ["./src/types/*"]
}
}
}
É possível também mapear um alias para múltiplos caminhos de fallback. O TypeScript tentará cada caminho na ordem definida:
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@shared/*": [
"./src/shared/*",
"./src/legacy/shared/*" // fallback
]
}
}
}
Para aliases aninhados ou subdiretórios específicos, podemos ser mais granulares:
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@components/ui/*": ["./src/components/ui/*"],
"@components/forms/*": ["./src/components/forms/*"],
"@utils/date": ["./src/utils/date/index.ts"], // alias específico
"@utils/validation": ["./src/utils/validation/index.ts"]
}
}
}
Boas práticas de nomenclatura incluem usar o prefixo @ para evitar conflitos com pacotes npm, ou usar nomes descritivos como utils/, components/ sem prefixo.
4. Como o TypeScript resolve os aliases
O TypeScript resolve os aliases seguindo uma ordem específica:
- Primeiro, calcula o caminho base a partir de
baseUrl - Depois, aplica o padrão definido em
paths
// Com baseUrl: "." e paths: { "@/*": ["./src/*"] }
// O import: import { x } from '@/utils/helper'
// Será resolvido para: ./src/utils/helper
É importante entender que paths não afeta a saída do JavaScript compilado. O TypeScript usa os aliases apenas para verificação de tipos e autocomplete no editor. O código JavaScript gerado mantém os caminhos originais com os aliases, o que causa erros em tempo de execução se não houver uma ferramenta de build configurada para resolvê-los.
A verificação de tipos funciona perfeitamente com aliases — o TypeScript entende os caminhos mapeados e fornece IntelliSense completo.
5. Integração com ferramentas de build e runtime
Para que os aliases funcionem em tempo de execução, precisamos configurar cada ferramenta separadamente:
Node.js com ts-node:
// Instalar: npm install tsconfig-paths
// Executar: node -r tsconfig-paths/register dist/index.js
// Ou no package.json:
{
"scripts": {
"start": "node -r tsconfig-paths/register dist/index.js"
}
}
Webpack:
// webpack.config.js
module.exports = {
resolve: {
alias: {
'@': path.resolve(__dirname, 'src/'),
'@components': path.resolve(__dirname, 'src/components/'),
'@utils': path.resolve(__dirname, 'src/utils/')
}
}
};
Jest:
// jest.config.js
module.exports = {
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1',
'^@components/(.*)$': '<rootDir>/src/components/$1',
'^@utils/(.*)$': '<rootDir>/src/utils/$1'
}
};
ESLint:
// .eslintrc.js
module.exports = {
settings: {
'import/resolver': {
typescript: {
alwaysTryTypes: true,
project: './tsconfig.json'
}
}
}
};
6. Aliases em monorepos e Project References
Em monorepos com Project References, a sincronização de paths entre projetos requer atenção especial:
// packages/shared/tsconfig.json
{
"compilerOptions": {
"composite": true,
"baseUrl": ".",
"paths": {
"@shared/*": ["./src/*"]
}
}
}
// apps/web/tsconfig.json
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@shared/*": ["../shared/src/*"],
"@web/*": ["./src/*"]
}
},
"references": [
{ "path": "../shared" }
]
}
Para evitar conflitos de alias entre pacotes, use prefixos únicos como @meuprojeto/shared e @meuprojeto/web. Isso mantém os aliases organizados e evita ambiguidades.
7. Problemas comuns e soluções
Erro "Cannot find module" mesmo com paths configurado:
- Verifique se o baseUrl está correto
- Confirme que o padrão * está sendo usado corretamente
- Reinicie o editor/TypeScript server (Ctrl+Shift+P → "TypeScript: Restart TS server")
Aliases não funcionam em tempo de execução:
- Lembre-se: paths é apenas para TypeScript em tempo de design
- Configure a ferramenta de build (Webpack, ts-node, etc.) para resolver os aliases
Conflitos com módulos npm:
- Use prefixos como @/ ou ~/ para diferenciar de pacotes npm
- Evite nomes genéricos como utils ou helpers
Dicas para debugar:
- Use tsc --traceResolution para ver como o TypeScript resolve cada módulo
- Verifique o arquivo compilado JavaScript para confirmar os caminhos gerados
8. Boas práticas e considerações finais
Quando usar aliases:
- Projetos com mais de 3 níveis de profundidade de diretórios
- Múltiplos módulos que compartilham imports comuns
- Monorepos com vários pacotes
Quando evitar aliases:
- Projetos pequenos com estrutura plana
- Imports que são usados apenas em um ou dois arquivos próximos
Manutenção:
- Centralize todos os aliases em um único tsconfig.json
- Evite criar aliases para cada subdiretório — mantenha um número gerenciável
- Documente os aliases no README do projeto
Alternativas:
- Use barrel files (index.ts) para organizar exports e reduzir a profundidade dos imports
- Considere Node.js subpath imports (campo imports no package.json) como alternativa moderna
Checklist para configurar paths em novos projetos:
1. Definir baseUrl no tsconfig.json
2. Configurar paths com os aliases necessários
3. Configurar Webpack/Vite/etc. com resolve.alias
4. Configurar Jest com moduleNameMapper
5. Configurar ESLint com import/resolver
6. Testar com um import de exemplo
7. Documentar os aliases para a equipe
Os aliases no TypeScript são uma ferramenta poderosa para melhorar a legibilidade e manutenibilidade do código. Quando configurados corretamente e integrados com as ferramentas de build, eles eliminam a fragilidade dos imports relativos longos e tornam o desenvolvimento mais produtivo.
Referências
- TypeScript Official Documentation: Module Resolution — Documentação oficial sobre mapeamento de caminhos e configuração de paths no TypeScript
- TypeScript Official Handbook: tsconfig.json paths — Referência completa da propriedade paths no tsconfig.json
- Webpack Documentation: Resolve Alias — Guia oficial do Webpack para configurar aliases de resolução de módulos
- Jest Documentation: moduleNameMapper — Documentação oficial do Jest para mapeamento de módulos com aliases
- tsconfig-paths npm package — Pacote que permite usar os paths do tsconfig.json em tempo de execução no Node.js
- ESLint Plugin Import: Resolver Typescript — Configuração do resolver TypeScript para o ESLint reconhecer aliases
- Microsoft TypeScript-React-Starter: Path Aliases Example — Exemplo prático de configuração de paths em um projeto React com TypeScript da Microsoft