尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

authentik Web 前端 tsconfig 拆分实战:深入理解 TypeScript extends 的“字段级替换“语义

authentik Web 前端 tsconfig 拆分实战:深入理解 TypeScript extends 的“字段级替换“语义 authentik Web 前端 tsconfig 拆分实战深入理解 TypeScript extends 的字段级替换语义【免费下载链接】authentikThe authentication glue you need.项目地址: https://gitcode.com/GitHub_Trending/au/authentik本文以 authentik 仓库web/docs/Changelog.md记录的 2024-03-26 变更为主线讲解 authentik 前端如何将 tsconfig 拆分为基础base与构建build两种变体、为何要把经验教训写在 Changelog 而不是 tsconfig 注释里以及 TypeScript 项目继承extends机制中最容易踩坑的既不是合并也不是替换语义。读完本文你将能正确设计自己的多级 tsconfig 继承链并避免在修改compilerOptions.paths时被静默覆盖。变更背景一次聚焦的 tsconfig 拆分web/docs/Changelog.md记录了 2024-03-26 的一次变更Split the tsconfig file into a base and build variant.即把原本单一的前端 tsconfig 文件拆分为base基础变体与build构建变体两个版本。这是 TypeScript 工程中常见的治理手段——开发环境与构建/CI 环境对类型检查范围、严格度、产物输出的要求往往不同用一个文件硬扛两种场景要么过度宽松、要么过度严格最终只能靠一堆注释和// TODO维持平衡。拆成两个文件后每个场景各取所需语义清晰。这份 Changelog 之所以值得单独成文是因为它在两行变更说明之外还沉淀了两条Lesson——一条关于JSON 格式的先天缺陷一条关于tsconfigextends的合并语义。这两条都是 authentik 前端团队踩坑后的真实记录且至今仍在该仓库的配置结构中留下痕迹可逐一对照验证。当前仓库中的真实形态base 与 build 两个变体拆分决策落实到当前仓库就是web/目录下的两个顶层文件web/tsconfig.json开发development用配置web/tsconfig.build.json构建build用配置。base 变体开发期的大而全web/tsconfig.json开头就写明// file TSConfig used by the web package during development.。它继承自 monorepo 基础包goauthentik/tsconfig并叠加了大量前端开发期需要的选项{ extends: goauthentik/tsconfig, compilerOptions: { ignoreDeprecations: 6.0, composite: true, types: [node], checkJs: true, allowJs: true, resolveJsonModule: true, allowSyntheticDefaultImports: true, emitDeclarationOnly: true, experimentalDecorators: true, target: esnext, module: preserve, moduleResolution: bundler, lib: [DOM, DOM.Iterable, ESNext] }, exclude: [ **/out/**/*, **/dist/**/*, storybook-static, src/**/*.test.ts, ./tests, packages/client-ts, packages/sfe, scripts/pseudolocalize.mjs, scripts/build-locales.mjs, src/**/*.comp.ts ] }几个值得注意的工程决策emitDeclarationOnly: true开发期只产出.d.ts声明文件实际 JS 由 esbuild见web/package.json中的build脚本负责打包experimentalDecoratorsuseDefineForClassFields: false与 web/package.json 中依赖的 Litlit ^3.3.3装饰器体系配套文件内还保留了指向https://lit.dev/docs/components/properties/的注释exclude排除了packages/client-tsOpenAPI 客户端和packages/sfe注释明确说明原因把这两个子包拉进来会让每次tsc -p .多检查约 1000 个文件Including them here pulls ~1k files ... into everytsc -p .for no benefit.文件里同时保留了// TODO:标记如noUncheckedIndexedAccess: false的放宽、src/**/*.comp.ts的清理说明这份配置仍处于演进中。build 变体只覆盖、不重复web/tsconfig.build.json则极尽精简全文件只有extends与exclude两处配置// file TSConfig used by the web package during build. { extends: ./tsconfig.json, exclude: [ src/**/*.test.ts, src/**/*.comp.ts, ./**/*.stories.ts, ./tests ] }这正是 Changelog 里拆分意图的落地build 变体以 base 变体为父只改写需要不同的部分——把测试文件、Storybook stories、待清理的旧组件*.comp.ts排除出构建时的类型检查范围而其余全部 compilerOptions 原样继承。base 与 build 的职责边界一目了然。基础包goauthentik/tsconfig两级变体的根是 monorepo 内的 TypeScript 基础配置包goauthentik/tsconfig源码在 packages/tsconfig/tsconfig.json。它的package.json描述为 authentiks base TypeScript configurationmain指向tsconfig.jsonweb/package.json中声明依赖goauthentik/tsconfig: ^1.0.9。基础包内固化了全仓统一的严格基线strict: true、alwaysStrict: true、noUncheckedIndexedAccess: true、useUnknownInCatchVariables: trueisolatedModules: true配合 esbuild/tsgo 等逐文件转译工具module/moduleResolution均为NodeNexttarget: ESNextcomposite: true、incremental: true、declarationdeclarationMap支持项目引用与增量构建outDir: ${configDir}/out的模板变量用法。教训一JSON 不支持注释元信息该放在哪Changelog 原文写道Lesson: This lesson is stored here and not in a comment in tsconfig.json because JSON doesnt like comments. Doug Crockfords purity requirement has doomed an entire generation to keeping its human-facing meta somewhere other than in the file where it belongs.这是一条带着怨念的工程文化记录JSON 规范不支持注释因此 tsconfig.json 里写不了对后续维护者友好的解释性文字——即使是 TSConfig 官方允许并推荐的//注释写法也只是 TypeScript 编译器在解析时的宽容而非 JSON 标准的一部分。一旦某个工具链用严格 JSON 解析器读取 tsconfig或开发者把配置片段复制进package.json这类纯 JSON 场景注释立刻变成语法错误。authentik 的选择是把这类为什么这样设计的元信息记入web/docs/Changelog.md而不是塞进配置文件。对照当前仓库这种配置文件只放机器要读的东西、人的叙事放文档的分工贯穿始终web/tsconfig.json 中连exclude的意图如排除packages/client-ts的理由、lit/localize-tools v0.8.0的 typing 问题都靠行内//注释说明——这是编译器能容忍的边界内注释而像为什么compilerOptions.paths必须整段复制这类无法用短注释讲清、又与 JSON 语法无关的教训则沉淀在 Changelog 中。工程启示配置文件里的注释是易腐资产——JSON 解析器不认识、格式化工具可能删除、复制粘贴时常丢失。对跨越重构周期仍有效的设计决策独立的文档文件是更可靠的载体。教训二extends 既不是 merge 也不是 replace而是字段级替换Changelog 中的第二条 Lesson 是全文的技术核心Lesson: Theextendcommand of tsconfig has an unexpected behavior. It is neither a merge or a replace, but some mixture of the two. The buildfilescompilerOptionsis not a full replacement; instead, each ofitstop-level fields is a replacement for what is found in the basefile. So while you dont need to includeeverythingin acompilerOptionsfield if you want to change one thing, if you want to modifyonepath incompilerOptions.path, you must include the entirecompilerOptions.pathcollection in your buildfile.这段话包含三层递进的事实可分别用仓库中的配置验证1. 顶层字段是替换而非合并当一个子配置extends父配置并写出compilerOptions时子配置的compilerOptions不会与父配置的compilerOptions做键级深合并——父的compilerOptions整体被子的compilerOptions取代。同理exclude、include等顶层字段也是整体替换。这正是 web/tsconfig.build.json 可以只写一个exclude就行的原因它没有提供compilerOptions所以父级base的compilerOptions原样生效。2. 好处只改一个选项不必重抄全部反过来说正因为是顶层字段级替换当你只想修改某几个 compilerOptions 时无需把父级的全部选项重抄一遍。例如web/test/unit/tsconfig.json继承goauthentik/tsconfig后只写了types、checkJs、allowJs、composite、resolveJsonModule、target、module、lib等自己关心的键其余严格性选项全部来自父级web/packages/core/tsconfig.json 甚至只覆写了lib、resolveJsonModule、checkJs、allowJs、emitDeclarationOnly五项。这就是不必包含所有内容的实践样本。3. 陷阱paths 必须整段重写但字段级替换的代价在paths原文档写作compilerOptions.path这类集合型键上暴露无遗paths本身是compilerOptions的一个顶层字段一旦子配置写出compilerOptions.paths父级的整个paths映射就会被整体丢弃——即使你只想新增一条别名映射也必须把父级paths的全部条目复制过来否则其余别名全部失效。Changelog 强调的就是这个不对称性标量/简单选项如target、module子级写出即覆盖无需重抄集合型选项paths同理还有plugins、lib等数组字段子级写出即整组替换必须整段继承再修改。从源码结构看这正是neither a merge or a replace, but some mixture of the two的准确含义它是顶层字段粒度的替换而非键粒度的合并。仓库中的额外佐证同构模式在web/packages/client-ts中还能看到第二个实例web/packages/client-ts/tsconfig.json 与 web/packages/client-ts/tsconfig.esm.json——后者只extends ./tsconfig.json并改写outDir为dist/esm同样遵循顶层字段替换的最小覆盖原则。这类一个基础配置 若干薄变体的用法在 authentik 前端 monorepo 中已被广泛采纳。如何在当前仓库中验证这套机制如果你在本地克隆了该仓库可以在web/目录下直接验证查看继承链的每一环web/tsconfig.build.json→web/tsconfig.json→goauthentik/tsconfigpackages/tsconfig/tsconfig.json运行类型检查脚本web/package.json中的lint:types执行tsc -p .基于 wireit 编排依赖build-localestsc脚本则使用tsgo -p .TypeScript 原生编译器预览版typescript/native-preview对照exclude差异base 与 build 变体对src/**/*.test.ts、src/**/*.comp.ts、./tests的处理不同可在两个文件间diff直观看到字段级替换的效果。小结web/docs/Changelog.md用一次拆分、两条 Lesson浓缩了 authentik 前端 TypeScript 工程化中的三个可复用结论按场景拆分 tsconfig开发期与构建期诉求不同base/build 变体各司其职构建变体只写差异JSON 不适合承载人的叙事设计决策类元信息应放在文档中配置文件只留机器所需理解extends的字段级替换语义改标量选项时无需重抄父级改paths等集合选项时必须整段包含——这是所有多级 tsconfig 继承项目中最高频、也最隐蔽的坑。这套经验在当前仓库的 web/tsconfig.json、web/tsconfig.build.json 与 packages/tsconfig/tsconfig.json 中均有完整落地可作长期参考。【免费下载链接】authentikThe authentication glue you need.项目地址: https://gitcode.com/GitHub_Trending/au/authentik创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表