El proyecto empieza con un tsconfig.json en la raíz. Después aparecen una
biblioteca compartida, una API, una aplicación web y pruebas que no deberían
entrar en el mismo build. El compilador sigue viendo una carpeta grande, el
editor abre archivos de proyectos distintos y el CI ejecuta un comando que no
representa la estructura real del repositorio.
Project References crean esa frontera. Convierte cada parte en un proyecto de
TypeScript compilable, declara las dependencias en tsconfig.json y usa tsc -b
para descubrir el orden. La función no sustituye a npm workspaces ni justifica
reorganizar cualquier repositorio pequeño.
Esta guía empieza con un monorepo mínimo y termina con criterios para decidir si la configuración merece mantenerse. El código es ilustrativo. No se ejecutó en un proyecto de producción. Los comandos de verificación muestran cómo probar el mismo esquema en tu repositorio.
Respuesta corta
- Usa Project References cuando tu repositorio tenga proyectos que necesiten fronteras, builds separados u orden explícito de dependencias.
- Activa
compositeen los proyectos referenciados, genera declaraciones y conserva untsconfigde solución confiles: [].- Ejecuta
tsc -bdesde la raíz. Eltscnormal no orquesta el grafo de referencias de la misma manera.- Si el código es pequeño y no tiene una frontera real entre proyectos, varios
tsconfigpueden añadir más trabajo que valor.
¿Qué necesitas antes de empezar?
Necesitas una versión LTS mantenida de Node.js, npm, un terminal y conocimientos
básicos de package.json y tsconfig.json. El ejemplo usa npm workspaces, pero
la idea del grafo también sirve para pnpm, Yarn o directorios que no se publican
como paquetes. Instala TypeScript en el workspace antes de ejecutar el script:
npm install --save-dev typescript @types/node
El objetivo es terminar con dos proyectos compilables, un directorio dist por
proyecto y una configuración de solución que puedas ejecutar desde la raíz. Si
tu repositorio usa un bundler, separa el typecheck del comando que empaqueta
JavaScript.
¿Cuándo merece la pena configurar Project References?
TypeScript introdujo Project References en la versión 3.0 para dividir los
programas en partes más pequeñas y trabajar con el modo --build (TypeScript,
“Project References”, consultado el 24/08/2026).
La función merece la pena cuando la división representa una dependencia real, no
solo una preferencia por carpetas más pequeñas.
Busca estas señales:
- una biblioteca debe compilarse antes de que una API pueda consumirla;
- las pruebas y el código de producción tienen entradas, salidas u opciones diferentes;
- el editor y el CI necesitan ver las mismas fronteras;
- un cambio en un paquete no debería reprocesar todos los demás;
- el equipo quiere que los archivos
.d.tssean la frontera pública de tipos de un proyecto.
No leas “builds más rápidos” como un benchmark. La documentación oficial explica cómo el modo de build comprueba qué proyectos están actualizados y compila los que necesitan trabajo, pero el resultado depende del grafo, el bundler y la cantidad de código. Mide tu repositorio cuando la configuración ya sea correcta.
La pregunta útil no es “¿cuántos paquetes tenemos?”. Es “¿qué proyecto debería
poder compilar sin abrir los detalles internos del otro?”. Si puedes nombrar al
menos una frontera, Project References puede expresar una decisión de
arquitectura. Si nadie puede nombrarla, el problema sigue siendo de organización,
no de tsconfig.
¿Qué resuelven npm workspaces y qué no resuelven?
npm define workspaces como funciones para gestionar varios paquetes locales desde
un paquete raíz. npm install crea los enlaces locales sin exigir llamadas
manuales a npm link (npm, “Workspaces”, consultado el 24/08/2026).
Eso resuelve la instalación y la resolución local de paquetes. No describe el
orden de compilación de TypeScript.
Piensa en estas responsabilidades como dos capas:
| Capa | Pregunta | Función |
|---|---|---|
| Paquetes | ¿Cómo aparece @acme/core en el node_modules local? |
npm workspaces |
| Compilación | ¿Qué debe construirse antes de apps/api? |
Project References |
| Tipos | ¿Qué salida representa la frontera de core? |
declaration y .d.ts |
| Orquestación | ¿Qué comando descubre el orden y omite proyectos actualizados? | tsc -b |
Un workspace puede enlazar core con api mientras el compilador sigue tratando
ambos como una sola lista de archivos. También es posible usar Project
References para organizar directorios sin npm workspaces. Combinar ambos es
habitual porque uno describe las relaciones entre paquetes y el otro los
proyectos de TypeScript.
Si estás empezando por la guía para crear un servidor MCP en TypeScript, probablemente baste una configuración. Cuando el servidor empieza a compartir código con otro paquete, la pregunta pasa a ser cuál es la frontera de compilación.
¿Cómo montar un monorepo mínimo de TypeScript?
Empieza con una biblioteca y una aplicación. Así el grafo queda visible y no introduces React, un bundler o una herramienta de monorepo antes de entender qué está haciendo el compilador.
.
├── package.json
├── tsconfig.json
├── packages/
│ └── core/
│ ├── package.json
│ ├── tsconfig.json
│ └── src/index.ts
└── apps/
└── api/
├── package.json
├── tsconfig.json
└── src/index.ts
En el package.json de la raíz, declara los workspaces y deja visible el
comando de build:
{
"name": "acme-workspace",
"private": true,
"workspaces": ["packages/*", "apps/*"],
"scripts": {
"build": "tsc -b tsconfig.json",
"build:verbose": "tsc -b tsconfig.json --verbose"
}
}
En el paquete core, configura la salida que leerá su consumidor:
{
"name": "@acme/core",
"version": "1.0.0",
"main": "dist/index.js",
"types": "dist/index.d.ts"
}
El paquete de la API puede depender del nombre del workspace:
{
"name": "@acme/api",
"version": "1.0.0",
"dependencies": {
"@acme/core": "1.0.0"
}
}
Añade una función pequeña en packages/core/src/index.ts para que la API tenga
una superficie pública concreta:
export function formatServiceName(service: string): string {
return service.toUpperCase();
}
npm documenta que una dependencia de workspace se enlaza localmente cuando el
paquete aparece en la configuración del workspace (npm, “Adding dependencies to
a workspace”, consultado el 24/08/2026).
El enlace local permite encontrar el paquete. La referencia en tsconfig sigue
siendo necesaria para que tsc -b conozca el orden.
¿Cómo declarar las referencias y ejecutar tsc -b?
Un proyecto referenciado necesita composite: true, y la raíz puede funcionar
como una solución sin archivos propios. La documentación de TypeScript también
recomienda una configuración con files: [] que apunte a los proyectos hoja
(TypeScript, “Project References”, consultado el 24/08/2026).
Crea primero la configuración de solución:
{
"files": [],
"references": [
{ "path": "./packages/core" },
{ "path": "./apps/api" }
]
}
El proyecto core necesita sus propias entradas, salidas y declaraciones:
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"strict": true,
"composite": true,
"declaration": true,
"declarationMap": true,
"rootDir": "src",
"outDir": "dist"
},
"include": ["src/**/*.ts"]
}
En apps/api/tsconfig.json, repite las opciones que deban mantenerse
consistentes y añade la dependencia de proyecto:
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"strict": true,
"composite": true,
"declaration": true,
"declarationMap": true,
"rootDir": "src",
"outDir": "dist"
},
"include": ["src/**/*.ts"],
"references": [
{ "path": "../../packages/core" }
]
}
La API puede importar el paquete por su nombre:
import { formatServiceName } from "@acme/core";
console.log(formatServiceName("billing"));
Después de instalar las dependencias y comprobar que TypeScript está disponible, ejecuta:
npm install
npx tsc -b tsconfig.json --verbose
El modo build encuentra los proyectos referenciados, comprueba cuáles están
actualizados y construye las dependencias en el orden correcto. También puede
recibir la ruta de un proyecto, como npx tsc -b apps/api.
El ejemplo es pequeño a propósito. En un proyecto real, centraliza las opciones
compartidas con extends, pero mantén coherentes rootDir, outDir y los
archivos incluidos en cada paquete. Un tsconfig.base.json compartido no debe
ocultar la dirección de las dependencias.
¿Qué cambia al activar composite, declaration y declarationMap?
composite añade restricciones que ayudan al compilador a determinar rápidamente
si un proyecto ya se construyó. El manual de TypeScript explica que los archivos
de implementación deben estar cubiertos por include o files, que rootDir
tiene otros valores predeterminados y que declaration se activa para un
proyecto referenciado (TypeScript, “Project References”, consultado el
24/08/2026).
Estas opciones forman parte del contrato:
compositeindica que el proyecto puede participar en un grafo de build;declarationgenera archivos.d.tspara la superficie tipada del consumidor;declarationMapconecta las declaraciones con el código para funciones como Go to Definition;outDirda a cada proyecto su propia salida y evita colisiones;tsBuildInfoFile, usado por configuraciones incrementales, guarda información para builds posteriores.
El TSConfig Reference de declarationMap
recomienda considerar esta opción cuando usas Project References. No puede
corregir un grafo equivocado. Si api referencia core pero importa archivos
internos mediante un alias que ignora la frontera del paquete, la declaración
generada no resolverá el problema arquitectónico.
Hay un coste práctico. Después de clonar el repositorio, los .d.ts esperados
pueden no existir. La documentación de TypeScript advierte que quizá debas
construir el proyecto o versionar ciertas salidas para que el editor navegue sin
errores falsos. Decide esta política como parte del flujo de desarrollo, no
después del primer error en el equipo.
Este artículo no tiene una prueba de producción que elija entre versionar dist
y generarlo después de clonar. Esa decisión depende del bundler, el entorno y la
política del repositorio. El punto verificable es más sencillo: un clon limpio
debe poder generar las declaraciones antes de que una herramienta dependiente
intente resolver el paquete.
¿Cómo comprobar que el grafo es correcto?
La configuración está lista cuando el comando de build demuestra la relación que declaran los archivos. Ejecuta un build detallado y lee qué proyectos estaban actualizados, cuáles se reconstruyeron y cuáles se omitieron:
npx tsc -b tsconfig.json --verbose --pretty false
Para simular la decisión sin escribir las salidas, usa --dry si la versión de
TypeScript instalada lo admite:
npx tsc -b tsconfig.json --dry --verbose
Después de la primera ejecución, revisa estas señales:
- Existe
packages/core/dist/index.d.tsy representa los exports públicos. apps/apicompila sin importarpackages/core/srcmediante una ruta relativa.- Cambiar solo
corehace que el build identifiqueapicomo dependiente cuando cambia la salida pública. - Quitar una entrada de
referencesproduce un error que muestra la dependencia ausente. - CI usa el mismo script de la raíz en lugar de un
tsc --noEmitque solo carga un proyecto.
Si el paquete usa un bundler, separa las tareas. El bundler puede generar el
JavaScript de la aplicación mientras tsc -b comprueba las fronteras y las
declaraciones. Un build correcto del bundler no demuestra que el grafo de Project
References esté bien.
Para una biblioteca consumida por otros paquetes, revisa también cómo publicas
los tipos. La guía para servir definiciones TypeScript de Eden Treaty
trata una frontera de paquete publicado. Aquí el foco es una frontera interna,
pero ambos problemas se encuentran cuando el .d.ts local se convierte en el
contrato que importa otro consumidor.
¿Por qué pueden discrepar el editor y el CI?
El editor puede abrir un archivo usando el tsconfig más cercano, mientras CI
ejecuta un comando desde la raíz. Project References hacen explícito el grafo,
pero no obligan a todas las herramientas a usar el mismo programa. TypeScript
advierte que los proyectos dependientes usan declaraciones construidas y que
tsc solo construye dependencias automáticamente cuando recibe --build
(TypeScript, “Caveats for Project References”, consultado el 24/08/2026).
Los síntomas habituales son:
- el editor navega hasta un tipo, pero
tsc --noEmitno encuentra el paquete; - la aplicación funciona después de un build manual, pero falla en un clon limpio;
- el lint con información de tipos lee fuentes mientras el compilador lee
.d.tsgenerados; - un alias de
pathsapunta asrcy evita la salida del paquete; - el proyecto está referenciado, pero no tiene
composite,includeuoutDircompatibles.
El issue sobre tipos que se resuelven como any entre paquetes con Project References
es un registro útil para diagnosticar este tipo de fricción. No es una regla para
todos los proyectos. Sirve para recordar que editor, linter, workspace y
compilador pueden resolver las fronteras de maneras diferentes.
Cuando una herramienta no entiende el grafo, elige una estrategia explícita: configura la herramienta para leer los proyectos correctos, ejecuta el build antes del análisis que depende de declaraciones o conserva una configuración de typecheck separada. No elimines las referencias solo para que pase un comando aislado sin entender qué estaba comprobando.
¿Cuándo no conviene usar Project References?
No uses Project References solo porque el repositorio tenga dos carpetas. Si hay
una aplicación pequeña, un proceso de build y ninguna frontera que el equipo
quiera proteger, un solo tsconfig.json puede ser más claro.
Aplaza la adopción cuando:
- el bundler todavía no puede consumir las nuevas salidas o aliases;
- los paquetes no tienen una relación de dependencia que alguien pueda explicar;
- el equipo no tiene un comando de clon limpio que genere las declaraciones necesarias;
- la única motivación es copiar la configuración de un monorepo mucho mayor;
- el problema real es un módulo mal organizado, no el alcance o la duración del typecheck.
Usa Project References cuando la frontera siga siendo útil aunque no mejore el tiempo. Una biblioteca que no debería importar la aplicación, un proyecto de pruebas con sus propias opciones o un paquete publicado internamente pueden justificar el grafo. Los builds incrementales son una posible consecuencia, no la única razón.
Si la arquitectura todavía no está clara, empieza por separar servicios y responsabilidades en el código. La guía de arquitectura de servicios con TypeScript trabaja en otro nivel. Project References hacen verificable una división existente, pero no crean una buena división por sí solas.
Preguntas frecuentes
¿Project References funcionan sin un monorepo?
Sí. La documentación de TypeScript describe referencias entre directorios que
tienen sus propios archivos tsconfig.json. Un monorepo con workspaces es una
combinación frecuente, pero no es un requisito. Necesitas proyectos separados de
TypeScript y una dependencia que quieras declarar.
¿Puedo usar tsc -p en vez de tsc -b?
Puedes usar tsc -p para compilar un proyecto, pero no es el orquestador del
grafo. Para construir dependencias en orden y reutilizar el estado conocido por
el modo build, usa tsc -b con la configuración de solución. Compara ambos
comandos con --verbose antes de elegir el script del CI.
¿Tengo que versionar los archivos .d.ts y dist?
No hay una respuesta universal. TypeScript advierte que los proyectos dependientes necesitan declaraciones para navegar y compilar. Puedes generarlas después de clonar, versionarlas o dejar que el entorno de desarrollo se encargue. Elige una política y haz que CI detecte un clon sin las salidas.
¿Project References sustituyen a Turborepo, Nx u otra herramienta de build?
No. Describen el grafo que entiende TypeScript. Una herramienta de build puede
gestionar caché, tareas, bundling, pruebas y ejecución entre paquetes. Empieza
con la función nativa cuando el problema sea la frontera de compilación. Añade
otra capa cuando exista una necesidad que tsc -b no cubra.
Conclusión
Project References le dicen a TypeScript que el repositorio contiene proyectos,
no solo carpetas. Workspaces enlaza paquetes locales. references declara quién
depende de quién. composite y las declaraciones hacen que la frontera sea
compilable. tsc -b demuestra el orden y encuentra qué debe reconstruirse.
Empieza con dos partes y un grafo pequeño. Ejecuta el build desde un clon limpio,
revisa los .d.ts, alinea editor y CI y solo después divide más proyectos. Si la
frontera no explica una decisión real del código, la configuración todavía no ha
resuelto el problema que te llevó a buscarla.
Fuentes consultadas
- TypeScript, “Project References”, consultado el 24/08/2026.
- TypeScript, “TSConfig Reference”, consultado el 24/08/2026.
- npm, “Workspaces”, consultado el 24/08/2026.
- TypeScript, “Modules: Reference”, consultado el 24/08/2026.
- Microsoft TypeScript, “Types resolving to
anyacross packages in monorepo using project references”, consultado el 24/08/2026.