Epic Stack 架构决策 001:TypeScript Only 技术路线的决策依据与工程落地
2026/9/17 10:34:57 网站建设 项目流程

Epic Stack 架构决策 001:TypeScript Only 技术路线的决策依据与工程落地

【免费下载链接】epic-stackThis is a Full Stack app starter with the foundational things setup and configured for you to hit the ground running on your next EPIC idea.项目地址: https://gitcode.com/GitHub_Trending/ep/epic-stack

本文以 Epic Stack 的首个架构决策记录(ADR-001)"TypeScript Only"为主体,完整还原这份决策的背景、决策内容与已接受的取舍,并结合当前仓库的源码与配置,展示"TypeScript Only"这一立场在共享类型配置、工具链脚本与质量校验层面是如何被真正落实的。读完后你能理解:一个全栈 Starter 为什么从一开始就封死 JavaScript 选项,以及这套"零配置 TypeScript"工程约束在代码库中的具体实现位置。

1. 决策档案:日期、状态与问题域

这份决策记录位于 docs/decisions/001-typescript-only.md,元信息为:

  • Date: 2023-05-08
  • Status: accepted

它属于 docs/decisions 目录下维护的架构决策记录(ADR)序列,该目录在 docs/decisions/README.md 中说明了自身定位:"记录我们为这个 starter 模板做的所有决策,方便日后弄清楚某些决策为何如此。"ADR-001 作为序列中的第一份决策,回答的是项目最基础的语言选择问题。

2. 背景(Context):create-remix 的 JavaScript 选项为何是个问题

原文档的 Context 部分给出了完整的推理链,核心要点如下:

  1. CLI 行为不可控。当时的create-remixCLI 允许用户选择"不用 TypeScript 而用 JavaScript",选择后 CLI 会把所有内容自动转换成 JavaScript。而项目方"(当时)没有任何办法控制这一行为"。
  2. TypeScript 的收益是普适的。团队和个人开发者构建现代 Web 应用时,使用 TypeScript 有充分理由。
  3. TypeScript 的两个经典挑战,以及本模板对它们的回应
    • 配置复杂:把 TypeScript 配置调对本身是个难题。而一个"从一开始就把你放在正确起点上、不需要你做任何配置"的技术栈天然消解了这个问题——这正是 Epic Stack 作为 Starter 的核心价值主张。
    • 非 TypeScript 依赖:处理未用 TypeScript 编写的第三方依赖曾是痛点,但随着越来越多依赖以 TypeScript 编写,这个问题正变得越来越小。

3. 决策(Decision):宁可报错,也不做 JavaScript 支持

决策部分的两条关键动作:

  1. 强烈建议使用 TypeScript,哪怕只是简单项目、哪怕只有单个开发者。因此,项目方不去"让这个项目兼容create-remix的 JavaScript 选项",而是直接抛出一个错误,提示用户重新运行并选择 TypeScript 选项。
  2. 在 README 的示例脚本中显式传入--typescript选项,使正常走示例的用户根本不会遇到那个提问;只有漏掉该 flag 时才会触发上述错误。

这是一种典型的"快速失败(fail fast)"策略:与其在模板中维护双语言兼容(.ts/.js 双份配置、双份构建逻辑、双份类型工具链),不如在初始化入口就把不符合约束的用法拦下来。

4. 后果(Consequences):明确接受的代价

ADR 如实记录了这一决策的负面后果,共三点:

  • 使用 JavaScript 的用户初始体验不佳(会被报错打断);
  • 作者希望 Remix CLI 未来能提供"是否询问 TypeScript 选项"的更细粒度控制,但目前无法控制;
  • 可能会激怒确实不喜欢 TypeScript 的人——对此,ADR 给出的官方答复是:"请自行 fork 这个 starter。"

把代价写进决策文档而不是回避,是这份 ADR 值得借鉴的地方:决策不是免费的,记录代价才能保证未来维护者在质疑这一约束时能追溯原始权衡。

5. 当前仓库中的落地证据:TypeScript Only 如何被工程化维持

ADR 是 2023 年(Remix 2 / create-remix 时代)作出的,而当前仓库已经演进到 React Router v7 + Vite 技术栈(package.json 中可见react-router: ^7.16.0vite: ^7.3.1)。用当前仓库源码可以验证,"TypeScript Only"这一立场并没有停留在纸面,而是沉淀成了几层可检查的工程约束。

5.1 类型基座:共享 tsconfig 与类型重置

ADR 中"不需要配置任何东西就站在正确起点上"的说法,在当前仓库中由 tsconfig.json 体现:

{ "include": ["**/*.ts", "**/*.tsx", ".react-router/types/**/*"], "extends": ["@epic-web/config/typescript"], "compilerOptions": { "types": ["@react-router/node", "vite/client"], "rootDirs": [".", "./.react-router/types"], "paths": { "@/icon-name": [ "./app/components/ui/icons/types.ts", "./types/icon-name.d.ts" ] } } }

可以看到,绝大多数编译选项通过extends委托给了@epic-web/config/typescript共享配置包——这正是 ADR 所承诺的"Stack 帮你把配置做对":用户拿到的不是"一个空 tsconfig",而是"一个已经调对的 tsconfig"。项目内仅需少量补充(如 tsconfig.json 中的rootDirs@/icon-name路径映射)。

类型层面还有专门的 types/ 目录承担基础设施职责:

  • types/reset.d.ts:仅两行,其中一行import '@epic-web/config/reset.d.ts'引入共享的类型重置(配合 package.json 中的@total-typescript/ts-reset依赖),用于把未知第三方库的类型收敛为安全的unknown,避免any泄漏——这是"处理非 TypeScript 依赖"这一 ADR 挑战在类型层的直接应对;
  • types/deps.d.ts、types/env.env.d.ts、types/icon-name.d.ts:分别处理依赖声明、环境变量类型、图标名称类型。

5.2 工具链全链路 TypeScript:从源码到脚本

当前仓库中,源码树几乎完全由 .ts/.tsx 构成app/下的全部路由(app/routes/**)、组件(app/components/**)、工具函数(app/utils/**),以及 prisma/seed.ts、vite.config.ts、playwright.config.ts、react-router.config.ts 等工程配置文件。即便是 Node 运行时入口 index.ts,也是 TypeScript 文件(index.ts 中用source-map-support还原 TypeScript 构建产物的堆栈,并通过MOCKS环境变量动态加载./tests/mocks/index.ts)。

package.json 中的依赖结构印证了这一点:

  • typescript: ^5.9.3(package.json),tsx: ^4.21.0允许直接以 TypeScript 执行脚本,例如 Prisma 种子脚本配置为"seed": "tsx prisma/seed.ts"(package.json);
  • 大量@types/*补齐非 TS 库的类型;测试、校验、构建工具(Vitest、Playwright、Vite)本身均以 TS 配置。

值得注意的是 package.json 中的两个脚本:

"typecheck": "react-router typegen && tsc", "validate": "run-p \"test -- --run\" lint typecheck test:e2e:run"

typecheck被并列编入validate一次性校验流水线(与单元测试、ESLint、E2E 测试并行运行)。这意味着"TypeScript Only"不只是语言偏好,而是质量门禁的一部分:类型错误会像测试失败一样阻断校验。

5.3 初始化入口:从"抛错拦截"到"模板本身即 TS"

ADR 描述的"抛出错误"逻辑,在 2023 年的 create-remix 初始化流程中执行。当前仓库中,初始化入口已迁移到 remix.init/index.mjs(由 remix.init/index.js 以 CommonJS 包装转发,README 中的初始化命令为npx epicli)。从当前源码结构看,remix.init/index.mjs中不再包含 TypeScript 选项检查或报错逻辑——因为模板本身已全量 TypeScript 化,约束不再需要运行时拦截来维持。

该初始化脚本当前承担的实际职责(见 remix.init/index.mjs#L34-L130):

  1. 生成随机应用名(目录名 + 随机后缀,remix.init/index.mjs#L40-L48),并同步替换 fly.toml 中的名称、package.json 的name字段;
  2. .env生成随机SESSION_SECRET(remix.init/index.mjs#L56-L59);
  3. 拷贝 remix.init/gitignore 为项目.gitignore,并清理模板专属文件;
  4. 依次执行npm run setup(即npm run build && prisma migrate deploy && prisma generate --sql && playwright install,见 package.json)与npm run format(remix.init/index.mjs#L99-L108);
  5. 可选引导 Fly.io 部署(含 staging 环境、密钥、卷、GitHub Action 配置)。

需要说明的适用前提:ADR 中提到的--typescriptflag 属于 create-remix 时代的初始化参数,当前仓库的初始化路径已演变为epicli+ 上述remix.init流程;但该决策的核心立场——不维护 JavaScript 变体、把报错/约束前移到入口——与现状是一致的。

6. 给使用者的实践要点

  • 采用本模板即默认全量 TypeScript,无需自行编写或调优 tsconfig:类型基线来自@epic-web/config,本地仅需关注 tsconfig.json 中的少量项目特化项。
  • 校验方式npm run typecheck单独跑类型检查(含 React Router 路由类型生成);npm run validate会并行执行测试、lint、typecheck 与 E2E,适合提交前做完整门禁。
  • 不打算使用 TypeScript 的开发者:按 ADR 的官方口径,选择 fork 后自行改造,而非在上游维护 JS 兼容。
  • 版本边界:本决策记录于 2023-05-08,其中 create-remix /--typescript的细节反映当时的初始化机制;当前仓库运行在 React Router v7 + Vite 之上(package.json 可查),阅读 ADR 时应以"决策立场"而非"CLI 参数"为准。

【免费下载链接】epic-stackThis is a Full Stack app starter with the foundational things setup and configured for you to hit the ground running on your next EPIC idea.项目地址: https://gitcode.com/GitHub_Trending/ep/epic-stack

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询