En proyectos con múltiples paquetes o workspaces, compilar todo desde cero en cada cambio es lento y los alias de rutas importadas con cadenas relativas largas como ../../../componentes/ui son difíciles de mantener. Las project references de TypeScript resuelven el primer problema mediante compilaciones incrementales, y los paths alias del tsconfig resuelven el segundo. Esta guía cubre ambas herramientas y cómo configurar los alias en Vite, webpack y esbuild.
Project references: composite y references
Para que un proyecto TypeScript pueda referenciar a otro, el proyecto referenciado debe tener composite: true en su tsconfig. Esto activa la generación de archivos .d.ts y de un fichero .tsbuildinfo para la compilación incremental:
// packages/utils/tsconfig.json (proyecto referenciado)
{
"compilerOptions": {
"composite": true,
"declaration": true,
"declarationMap": true,
"outDir": "./dist",
"rootDir": "./src"
},
"include": ["src"]
}
// packages/app/tsconfig.json (proyecto que referencia)
{
"compilerOptions": {
"outDir": "./dist",
"rootDir": "./src"
},
"references": [
{ "path": "../utils" } // ruta al tsconfig del proyecto referenciado
],
"include": ["src"]
}
// Compilar con referencias (incremental, solo lo que cambió):
// tsc --build packages/app
tsconfig raíz con extends
En un monorepo con muchos paquetes es habitual tener un tsconfig base compartido y extenderlo en cada paquete:
// tsconfig.base.json (raíz del monorepo)
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true
}
}
// packages/api/tsconfig.json
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"composite": true,
"outDir": "./dist",
"rootDir": "./src"
}
}
// tsconfig.json raíz (solo para el IDE, no para compilar)
{
"files": [],
"references": [
{ "path": "packages/utils" },
{ "path": "packages/api" },
{ "path": "packages/web" }
]
}
paths alias con baseUrl
Los paths alias permiten usar @/componentes/Boton en lugar de ../../componentes/Boton. Se configuran con baseUrl y paths en el tsconfig:
// tsconfig.json
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"],
"@componentes/*": ["src/componentes/*"],
"@utils/*": ["src/utils/*"],
"@tipos": ["src/types/index"]
}
}
}
// Ahora en cualquier archivo:
import { Boton } from "@componentes/Boton";
import { formatearFecha } from "@utils/fecha";
import type { Usuario } from "@tipos";
Configurar los alias en Vite
// vite.config.ts
import { defineConfig } from "vite";
import path from "path";
export default defineConfig({
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),
"@componentes": path.resolve(__dirname, "./src/componentes"),
"@utils": path.resolve(__dirname, "./src/utils"),
},
},
});
// También puedes usar el plugin vite-tsconfig-paths para leerlos del tsconfig:
// import tsconfigPaths from "vite-tsconfig-paths";
// plugins: [tsconfigPaths()]
Configurar los alias en webpack
// webpack.config.js
const path = require("path");
module.exports = {
resolve: {
alias: {
"@": path.resolve(__dirname, "src"),
"@componentes": path.resolve(__dirname, "src/componentes"),
},
extensions: [".ts", ".tsx", ".js"],
},
};
Configurar los alias en esbuild / tsconfig-paths
// Para Node.js en runtime (no solo compilación):
// npm install -D tsconfig-paths
// Ejecutar con alias resueltos:
// node -r tsconfig-paths/register dist/index.js
// O en el entry point:
// tsconfig-paths/register debe llamarse antes de los imports
// Con tsx (ejecución directa sin compilar):
// tsx --tsconfig tsconfig.json src/index.ts (alias resueltos automáticamente)
// Con esbuild:
import { build } from "esbuild";
import { TsconfigPathsPlugin } from "@esbuild-plugins/tsconfig-paths";
await build({
entryPoints: ["src/index.ts"],
bundle: true,
plugins: [TsconfigPathsPlugin({ tsconfig: "./tsconfig.json" })],
});
Las project references y los paths alias son dos mejoras independientes que se complementan bien en monorepos grandes. Las references resuelven el rendimiento de la compilación; los alias resuelven la legibilidad de los imports. Ambas requieren configuración tanto en TypeScript como en la herramienta de build que uses, ya que TypeScript solo las gestiona a nivel de tipos, no en el bundling final.
Imagen: Pexels / Tima Miroshnichenko
