Cómo configurar un proyecto TypeScript con tsconfig.json | Nicolás Garzón
tsconfig.json define qué archivos forman el proyecto y bajo qué reglas TypeScript debe analizarlos o emitirlos.
Texto
Copiar tsconfig.json
├─ archivos incluidos
├─ entorno objetivo
├─ sistema de módulos
├─ nivel de comprobación
├─ estrategia de emisión
└─ declaraciones globales disponiblesBash
Copiar npm install --save-dev typescriptUtilizar la instalación del proyecto:
Bash
Copiar npx tsc --version Esto reduce diferencias entre máquinas, CI y editor.
El archivo generado es un punto de partida, no una configuración universal para cualquier framework o runtime.
Bash
Copiar npx tsc -p tsconfig.json --noEmit JSON
Copiar {
"scripts" : {
"typecheck" : "tsc -p tsconfig.json --noEmit"
}
} JSON
Copiar {
"compilerOptions" : { } ,
"include" : [ "src" ] ,
"exclude" : [ "dist" ]
} Reglas del checker, resolución y emisión.
Patrones que forman parte del proyecto.
Evita incluir rutas encontradas mediante include. No es una barrera absoluta: un archivo importado puede entrar al programa aunque coincida con exclude.
Lista explícita de archivos raíz. Es útil en proyectos pequeños o generados, pero suele ser menos cómoda que include.
JSON
Copiar {
"compilerOptions" : {
"target" : "ES2022" ,
"module" : "preserve" ,
"moduleResolution" : "bundler" ,
"strict" : true ,
"noEmit" : true ,
"verbatimModuleSyntax" : true
} ,
"include" : [ "src" ]
}
El bundler conserva la responsabilidad de transformar y empaquetar.
TypeScript comprueba imports según un entorno de bundling moderno.
noEmit evita generar otra copia de JavaScript.
El framework puede proporcionar un tsconfig base. Extiéndelo en lugar de reemplazar sus decisiones sin entenderlas.
JSON
Copiar {
"compilerOptions" : {
"target" : "ES2022" ,
"module" : "nodenext" ,
"moduleResolution" : "nodenext" ,
"strict" : true ,
"outDir" : "dist" ,
"rootDir" : "src"
} ,
"include" : [ "src" ]
} nodenext intenta reflejar cómo Node interpreta ESM y CommonJS según extensiones y package.json.
No copies una configuración de Vite a Node ni una de Node a Next.js esperando el mismo contrato.
JSON
Copiar {
"compilerOptions" : {
"strict" : true
}
} Activa una familia de comprobaciones más rigurosas.
Aunque una versión futura cambie defaults, declararlo explícitamente comunica que el proyecto depende de esas garantías.
Puede incluir reglas relacionadas con:
null y undefined.
Parámetros implícitamente any.
Uso de this.
Compatibilidad de funciones.
Propiedades de clases.
Variables de catch.
JSON
Copiar {
"compilerOptions" : {
"target" : "ES2022"
}
} Afecta la sintaxis emitida y las libs predeterminadas asociadas.
Selecciona un target compatible con el runtime o pipeline final, no con el navegador que utilizas personalmente.
JSON
Copiar {
"compilerOptions" : {
"module" : "preserve"
}
} Comunica cómo se deben interpretar o emitir imports y exports.
Para proyectos modernos suelen aparecer:
preserve o esnext cuando un bundler procesa módulos.
nodenext para Node moderno.
JSON
Copiar {
"compilerOptions" : {
"moduleResolution" : "bundler"
}
} Define cómo TypeScript encuentra archivos y tipos a partir de un import.
TypeScript
Copiar import { createClient } from "@app/client" ; Que el editor encuentre el tipo no garantiza que el runtime o bundler conozca el mismo alias. Todas las herramientas deben compartir la resolución.
Texto
Copiar bundler → preserve/esnext + bundler
Node moderno → nodenext + nodenextUna combinación incoherente puede mostrar imports válidos en el checker que fallan en ejecución.
JSON
Copiar {
"compilerOptions" : {
"noEmit" : true
}
} Útil cuando otra herramienta genera output.
No significa “no compilar”; TypeScript todavía construye el programa y comprueba tipos.
JSON
Copiar {
"compilerOptions" : {
"rootDir" : "src" ,
"outDir" : "dist"
}
} Son relevantes cuando tsc emite archivos.
rootDir no decide qué archivos se comprueban; describe la raíz esperada de la estructura emitida.
JSON
Copiar {
"compilerOptions" : {
"lib" : [ "ES2022" , "DOM" ]
}
} Declara qué APIs globales puede asumir el código.
Aplicación web: ECMAScript + DOM.
Worker: ECMAScript + WebWorker.
Node: ECMAScript + tipos de Node instalados.
No incluyas DOM en una librería universal únicamente para silenciar errores.
JSON
Copiar {
"compilerOptions" : {
"types" : [ "node" , "vitest/globals" ]
}
} Controla paquetes de tipos que añaden declaraciones globales.
En versiones modernas los defaults han cambiado, por lo que es mejor declarar los globals que realmente utiliza cada configuración.
JSON
Copiar {
"extends" : "./tsconfig.base.json" ,
"compilerOptions" : {
"noEmit" : true
}
} Permite compartir decisiones entre aplicaciones, tests y packages.
No conviertas el archivo base en una mezcla de DOM, Node, Jest y cualquier entorno; cada proyecto debe declarar sus globals.
Texto
Copiar tsconfig.base.json
├─ tsconfig.app.json
├─ tsconfig.node.json
├─ tsconfig.test.json
└─ tsconfig.build.jsonAporta cuando los entornos tienen libs, tipos o emisión diferentes.
JSON
Copiar {
"compilerOptions" : {
"skipLibCheck" : true
}
} Omite comprobar internamente todos los .d.ts, lo que puede mejorar tiempos y evitar conflictos transitivos.
No corrige declaraciones incorrectas. Tu código sigue siendo comprobado contra las formas resultantes y un problema real de versiones puede permanecer oculto.
noUncheckedIndexedAccess.
exactOptionalPropertyTypes.
noImplicitOverride.
useUnknownInCatchVariables.
noUncheckedSideEffectImports.
isolatedDeclarations.
Primero debes entender qué contrato modifica cada uno; activar todo sin modelo puede generar assertions para callar al compilador.
Texto
Copiar TypeScript: Select TypeScript Version
→ Use Workspace VersionEl editor debe utilizar la misma versión que CI siempre que el flujo lo permita.
La versión 7 utiliza un compilador y language server nativos, pero algunos frameworks y herramientas que dependen de la API programática pueden continuar temporalmente sobre TypeScript 6.
La regla práctica es verificar:
Versión soportada por el framework.
Plugin del editor.
Linter.
Generadores de declarations.
Build y CI.
No actualices únicamente porque npm ofrece una versión más nueva.
JSON
Copiar {
"scripts" : {
"dev" : "vite" ,
"typecheck" : "tsc -p tsconfig.app.json --noEmit" ,
"build" : "npm run typecheck && vite build"
}
} La build no depende de que el bundler realice comprobación completa por accidente.
Utilizar TypeScript global en vez del instalado.
Copiar un tsconfig sin conocer el runtime objetivo.
Confundir exclude con una prohibición total.
Configurar aliases solo en TypeScript.
Usar DOM en código Node para silenciar errores.
Dejar que bundler y tsc emitan la misma aplicación.
Depender de defaults que cambian entre versiones.
Activar flags estrictos y responder con as en cada error.
Actualizar el compilador sin comprobar tooling.
Creer que skipLibCheck arregla incompatibilidades.
tsconfig describe el proyecto completo.
Usa la instalación local de TypeScript.
strict debe ser una decisión explícita.
module y moduleResolution representan cómo se ejecutará el código.
noEmit separa checking de transformación.
lib y types controlan declaraciones globales.
include define raíces, pero imports también incorporan archivos.
Los entornos distintos merecen configuraciones distintas.
La versión del compilador y del tooling debe ser compatible.
¿Por qué un alias configurado únicamente en paths puede funcionar en el editor y fallar al ejecutar?
Respuesta Porque TypeScript puede utilizarlo para resolver tipos, pero el runtime o bundler necesita su propia configuración equivalente para encontrar el archivo real.
Inferencia, anotaciones y contextual typing explica cuándo el compilador puede deducir un tipo y cuándo conviene declarar el contrato.