Nuxt 的 tsconfig.json 机制解析:四个自动生成配置文件与 nuxt.config.ts 定制指南
Nuxt 不会让你手写一份大而全的 tsconfig.json,而是按运行上下文自动拆分生成 tsconfig.app.json、tsconfig.server.json、tsconfig.node.json 和 tsconfig.shared.json 四个文件,项目根目录只需放置一个基于 references 的“壳”配置。本文基于仓库中 tsconfig 文档 展开,结合 类型生成源码 与 Nitro 服务端集成代码,讲清楚这四个文件各自覆盖哪些代码、默认的 compilerOptions 是什么、以及如何在 nuxt.config.ts 中正确地扩展它们而不破坏 Nuxt 或模块依赖的内部约定。
项目根目录:一个只含 references 的 tsconfig.json
Nuxt 会在 .nuxt 目录下自动生成多份 TypeScript 配置(.nuxt/tsconfig.app.json、.nuxt/tsconfig.server.json、.nuxt/tsconfig.node.json 和 .nuxt/tsconfig.shared.json),每份都包含 Nuxt 推荐的基线 compilerOptions,并进一步写入 自动导入 的类型引用、类型化 API 路由 的声明、路径别名(paths)等内容。
因此,你的 Nuxt 项目根目录应当只放置这样一个文件:
{
"files": [],
"references": [
{ "path": "./.nuxt/tsconfig.app.json" },
{ "path": "./.nuxt/tsconfig.server.json" },
{ "path": "./.nuxt/tsconfig.shared.json" },
{ "path": "./.nuxt/tsconfig.node.json" }
]
}
注意:不推荐直接修改根目录这个文件的 contents,因为这样做可能覆盖 Nuxt 或其他模块依赖的重要设置。正确的做法是通过
nuxt.config.ts扩展生成结果(见下文“通过 nuxt.config.ts 定制”一节)。
这种“根配置只做 references 分发”的设计是 TypeScript Project References 的典型用法:每个上下文拥有独立的 compilerOptions 与 include 边界,编辑器可以按文件所属上下文应用正确规则。更多类型上下文背景见 TypeScript 概念文档 的 project references 部分。
从源码看,根目录文件本身不含任何编译规则("files": [] 表示它不直接编译文件),真正干活的是四个引用目标。Nuxt 在构建流程中明确维护了一份“受管理文件”清单,builder.ts 中写有:
const generatedTsConfigs = ['tsconfig.json', 'tsconfig.app.json', 'tsconfig.server.json', 'tsconfig.node.json', 'tsconfig.shared.json']
这说明这些文件(包括 .nuxt/tsconfig.json 这个兼容旧版的 legacy 文件)全部由框架托管,手动改动会在下次类型重新生成时被覆盖。
四个 tsconfig 文件分别覆盖什么
各上下文的职责可以从生成逻辑直接确认。packages/kit/src/template.ts 中的 writeTypes 负责写出 app/node/shared 三份配置(template.ts#L664-L688):
const appTsConfigPath = resolve(typesDir, 'tsconfig.app.json')
const legacyTsConfigPath = resolve(typesDir, 'tsconfig.json')
const nodeTsConfigPath = resolve(typesDir, 'tsconfig.node.json')
const sharedTsConfigPath = resolve(typesDir, 'tsconfig.shared.json')
而 tsconfig.server.json 由 Nitro 层写出:nitro-server/src/index.ts 将 tsconfigPath 指向 join(typesDir, 'tsconfig.server.json'),并把 server 目录、shared 声明等纳入扫描范围。
| 生成的文件 | 覆盖的代码上下文 | 主要 include 内容(源码推导) |
|---|---|---|
tsconfig.app.json |
Vue 应用代码(客户端 + SSR 组件) | app/、层目录中的 app/**、模块 runtime,以及 .nuxt/nuxt.d.ts |
tsconfig.server.json |
Nitro 服务端(server/ 目录、API 路由) |
由 Nitro 集成写入,含 server/**、types/nitro 等 |
tsconfig.node.json |
加载 nuxt.config.ts 与模块的环境(Node) |
nuxt.config.*、modules/*.*、.nuxt/nuxt.node.d.ts |
tsconfig.shared.json |
shared/ 目录(客户端与服务端共享代码) |
shared/**、模块 shared/**、.nuxt/nuxt.shared.d.ts |
include 的具体模式由 resolveLayerPaths 统一计算,例如 node 上下文固定包含 nuxt.config.*、layers/*/nuxt.config.*、layers/*/modules/*.*,shared 上下文固定包含各层的 shared/**。这样即使使用了 layers,每个上下文的类型边界也不会互相污染。
值得注意的是写入策略:writeTypes 内部使用 writeIfChanged 只有内容变化才落盘(template.ts#L695-L699),源码注释解释了原因——类型文件每次启动都会重新生成且通常完全一致,保持 mtime 不变可以让编辑器和 tsc --watch 跳过重复工作。
默认 compilerOptions:Nuxt 替你做了哪些决定
_generateTypes 中通过 defu 合并出了一份基线配置(template.ts#L409-L465),核心默认值包括:
- 严格度:
strict(可通过typescript.strict关闭,默认true)、noUncheckedIndexedAccess、noImplicitOverride、forceConsistentCasingInFileNames;future.compatibilityVersion >= 5时还会追加noUncheckedSideEffectImports。 - 模块系统:
module: 'preserve'(检测到 TypeScript >= 5.4 时,否则回退ESNext)、verbatimModuleSyntax、isolatedModules、moduleDetection: 'force'、esModuleInterop、resolveJsonModule。 - 不产出产物:
noEmit: true——类型检查交给编辑器或vue-tsc,构建由 Vite/Webpack/Rspack 完成。 - 关闭环境类型自动扫描:
types: [],所有第三方/模块类型必须通过显式references引入(写入nuxt.d.ts/nuxt.node.d.ts/nuxt.shared.d.ts声明文件),避免 Node、DOM 等全局类型在错误的上下文里泄漏。 - 路径别名:
paths初始为空对象,随后由 Nuxt 遍历nuxt.options.alias填充,把~、@、#build、layer 别名等全部转成相对.nuxt目录的路径,并按固定顺序排序(sortTsPaths:typescript.hoist的包路径置顶,layer 别名居中,#build置底)。
文档中提到的两类“例外”,在源码里有一一对应:
- DOM/Vue 专属选项只作用于 app。源码中明确列出了应用专属选项清单(template.ts#L473-L480):
const appOnlyCompilerOptions = ['lib', 'libReplacement', 'jsx', 'jsxImportSource', 'noUncheckedSideEffectImports', 'experimentalDecorators'] as const
const nonAppCompilerOptions = (): NonNullable<TSConfig['compilerOptions']> => {
const compilerOptions = { ...baseTsConfig.compilerOptions }
for (const key of appOnlyCompilerOptions) {
delete compilerOptions[key]
}
return { ...compilerOptions, noEmit: true, types: [], paths: {} }
}
也就是说,即使你在 typescript.tsConfig 里设置了 lib、jsx、jsxImportSource,node/shared/server 上下文也会在生成时被剥离掉这些选项。
types、paths、noEmit由 Nuxt 按上下文管理。nonAppCompilerOptions()强制noEmit: true、types: []、paths: {}(随后由别名填充逻辑重写),所以在全局tsConfig里设置这三项对非 app 上下文无效——必须用对应的按上下文选项覆盖。
通过 nuxt.config.ts 定制 TypeScript 配置
所有定制入口都在 nuxt.config.ts 的 typescript 块中:typescript.tsConfig 为所有上下文设置共享的 compilerOptions,而 appTsConfig、sharedTsConfig、nodeTsConfig、serverTsConfig 分别针对单个上下文覆盖。
export default defineNuxtConfig({
typescript: {
// shared compiler options for every generated tsconfig
tsConfig: {
compilerOptions: {
// ...
},
},
// customize tsconfig.app.json
appTsConfig: {
// ...
},
// customize tsconfig.shared.json
sharedTsConfig: {
// ...
},
// customize tsconfig.node.json
nodeTsConfig: {
// ...
},
// customize tsconfig.server.json
serverTsConfig: {
// ...
},
},
})
合并优先级可以直接从 defu 调用顺序读出(template.ts#L409-L499):
const baseTsConfig: TSConfig = defu(nuxt.options.typescript?.tsConfig, { /* Nuxt 默认值 */ })
const tsConfig: TSConfig = defu(nuxt.options.typescript?.appTsConfig, baseTsConfig) // app 上下文
const nodeTsConfig: TSConfig = defu(nuxt.options.typescript?.nodeTsConfig, { ... }) // node 上下文
const sharedTsConfig: TSConfig = defu(nuxt.options.typescript?.sharedTsConfig, { ... }) // shared 上下文
即:按上下文的选项(appTsConfig 等) > 全局 tsConfig > Nuxt 内置默认值。schema 中的注释(schema.ts#L1953-L1982)同样说明:tsConfig 的 compilerOptions 对所有生成的 tsconfig 生效,而 include、exclude 和 vueCompilerOptions 只作用于 tsconfig.app.json(及 legacy 的 .nuxt/tsconfig.json)。
另外两个与文档一致的行为值得注意:
- server 上下文有双入口:
typescript.serverTsConfig与nitro.typescript.tsConfig都会扩展tsconfig.server.json并保持同步,设置任意一个效果相同。官方建议优先使用typescript.serverTsConfig,把四个上下文的定制集中在一个位置。 - 其他可用的
typescript选项(默认值见 config/typescript.ts):strict(默认true)、typeCheck(默认false,设为true或'build'可启用类型检查,需安装typescript与vue-tsc)、hoist(为 pnpm 等严格安装场景生成深路径别名,默认包含vue、nuxt等核心依赖)、shim(是否生成.vue声明文件,默认false)、includeWorkspace(把父 workspace 纳入类型范围,模块/主题作者常用,默认false)。
类型引用:nuxt.d.ts 与模块类型如何进入上下文
四个 tsconfig 的 include 起点各不相同(./nuxt.d.ts、./nuxt.node.d.ts、./nuxt.shared.d.ts),这些声明文件由同一次生成流程写入,内容是 Nuxt 收集到的 TSReference(/// <reference types="..." />)与自定义声明。模块安装时,Nuxt 会把每个模块包注册为类型引用(template.ts#L557-L572),分别加入 app 上下文与 node 上下文的声明文件——这就是为什么安装一个模块后无需手动 import 就能在模板和配置里用它的类型。
prepare:types 钩子(template.ts#L576)则允许模块在写盘前向这些引用和声明追加内容,Nitro 正是通过同类机制在写 .nuxt/tsconfig.server.json 前注入自定义 references 与声明(augments.ts#L86)。
验证与参考
- 类型生成的行为有专门测试覆盖,包括各上下文 tsconfig 与声明文件的产出:packages/kit/test/generate-types.spec.ts;模块级 tsconfig 相关断言另见 packages/kit/test/module.test.ts。
- 本仓库(Nuxt 自身)的根 tsconfig.json 展示了与生成默认值一致的严格基线写法(
strict、noUncheckedIndexedAccess、verbatimModuleSyntax、module: 'preserve'、noEmit等),可当作一份人工项目配置的对照样本。 - 概念层面的背景(自动生成的类型、project references、类型化 API 路由)见 docs/3.guide/1.concepts/8.typescript.md 与 docs/3.guide/1.concepts/4.server-engine.md。
总结一句话:把根 tsconfig.json 当作只读分发器,所有 TypeScript 定制都走 nuxt.config.ts 的 typescript 块;记住 DOM/Vue 选项只进 app、types/paths/noEmit 按上下文管理、server 定制优先用 serverTsConfig,就能在享受 Nuxt 生成的完整类型体系的同时保持可控的覆盖能力。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00