TypeScript 编码
工作流
-
了解项目。 在编写代码之前,先阅读项目的权威来源:
package.json(type、engines、packageManager、exports、scripts、已安装的主要版本)、lockfile、每个tsconfig*.json及其extends链、根工作区、框架、lint 和格式配置(pnpm-workspace.yaml、turbo.json、next.config.ts、biome.json、.oxlintrc.json),以及 CI 中的规范检查命令。运行tsc --showConfig来解析extends链;它会省略那些保持默认值的选项,因此请从references/modules.md(TypeScript 6 和 7 已更改默认值)中读取这些选项。阅读你正在修改的模块及其相邻模块;重用它们的辅助函数、错误类型、模式和模式。 完成条件: 你能够说出包管理器、TypeScript 主版本号、module和moduleResolution、strict之外的严格标志(noUncheckedIndexedAccess、exactOptionalPropertyTypes、erasableSyntaxOnly)、lint 和格式工具链,以及项目的检查命令。 -
设计类型。 先设计数据,再设计控制流,遵循 类型 的规则。来自进程外部的输入:阅读
references/zod.md。修改包的公开 API:阅读references/api.md。 完成条件: 变更所依赖的每个不变量都用类型编码,并且每个跨越信任边界的值在使用前都经过模式解析。 -
紧凑循环实现。 以小步骤工作,并在每一步之后运行
tsc --noEmit(对于有项目引用的项目,使用tsc -b)。阅读完整的诊断信息;其代码(如TS2742)是搜索references/modules.md和references/types.md中表格的关键。对于依赖项的 API,在node_modules/<pkg>/下阅读已安装版本的.d.ts文件。Bug 修复:先编写回归测试并观察它失败。 完成条件: 变更在类型检查时没有新的诊断信息,并且任何回归测试在修复前变红,修复后变绿。 -
验证。 通过包管理器(
pnpm exec、npx、bunx)运行步骤 1 中的项目命令;否则,针对项目使用的工具链,运行以下命令:tsc --noEmit # 当 tsconfig.json 仅列出引用时,使用 tsc -b biome ci # Biome oxfmt --check && oxlint --type-aware --deny-warnings # oxlint + oxfmt vitest run --typecheck # 或项目的测试运行器- 根目录下的
tsconfig.json如果包含"files": []和references,会使tsc --noEmit什么都不检查;应使用tsc -b。在没有根 tsconfig 的工作区中,通过任务运行器(turbo run check-types test)在每个包中运行检查。 - 在每个 linter 的循环中都要保留
tsc:Biome 的类型推断和 oxlint 的类型感知规则是对它的补充,绝不能替代它。 - 格式化失败:应使用格式化器(
biome check --write、oxfmt)应用格式,而不是手动编辑。 - 在根本原因处修复诊断信息。当必须抑制时,将其范围限定在一行内并给出理由:
// biome-ignore lint/<group>/<rule>: reason、// oxlint-disable-next-line <rule> -- reason、// @ts-expect-error reason。 - 如果修改了 lint 或格式配置:对照
references/lints.md进行检查,然后对整个仓库运行 linter 和格式化器,而不仅仅是修改过的文件。 - 如果修改了已发布包的公共 API:运行
references/api.md。 完成条件: 每个命令都返回 0,你修改的行没有产生任何警告,并且每个新的抑制都带有理由。
- 根目录下的
-
自审差异。 重新阅读完整的
git diff(对于新文件,使用git add -A && git diff --cached),而不是凭记忆,并对照 规则 以及 diff 涉及到的每个参考文件的规则进行检查。 完成条件: 每个块都是任务必需的,console.log、debugger、.only和注释掉的代码已被删除,文档注释与行为一致,并且所有适用的规则都适用于每个块。
审查 他人的代码:执行步骤 1,在可能的情况下执行步骤 4 的命令,然后将步骤 5 应用于他们的 diff。将每个发现报告为 path:line、规则和修复方法。
规则
类型
- 用证据缩小类型范围:
typeof、instanceof、in、鉴别检查、类型谓词或模式解析。禁止使用any、非空断言!和as T(测试中也禁止);保留as const和satisfies。当库的类型错误时,使用模块扩充在源头修复它们 (references/types.md)。 - 使非法状态不可表示:使用带字面量
kind字段的鉴别联合,而不是boolean标志加可选字段。通过模式(z.string().brand<"UserId">())或类型谓词对 ID、单位和已验证的值进行品牌化,绝不要使用as。 - 解析,而非验证:
JSON.parse、Response.json()、环境变量、存储、URL 参数和postMessage数据在边界处被 Zod 模式解析前都是unknown;将解析后的类型向内传递。 - 对自己拥有的联合类型使用穷举 switch:以
default: x satisfies never; throw new Error(...)结束,或者使用Record<Union, V>查找表,当添加新成员时会导致编译失败。 - 在
noUncheckedIndexedAccess下,索引访问返回T | undefined:处理undefined(使用守卫、?? fallback、for...of),而不是断言它不存在。 - 导出的函数声明其返回类型;局部变量保持推断。
- 对象形状使用
interface,使用extends而非&交叉类型;联合、映射和条件类型使用type。 - 对于函数不进行修改的输入,接受
readonly T[]和Readonly<T>。 - 优先使用字符串字面量联合或
as const对象,而不是enum和带有运行时代码的命名空间(两者在erasableSyntaxOnly下都被禁止)。
错误与异步
- 调用者必须处理的预期失败返回一个结果联合;错误和不可恢复的状态抛出一个带有
{ cause }的Error子类。详情:references/errors.md。 - 每个 Promise 都必须被 await、返回或传递给一个观察其拒绝的处理程序。
当前惯用法
训练数据早于 TypeScript 6 和 7:TypeScript 7 是基于 Go 的编译器,strict 默认开启,types 默认为 [],并且 baseUrl、outFile、target: "ES5" 和 moduleResolution: "node10" 已被移除 (references/modules.md)。在已安装版本允许的情况下,优先使用当前形式,包括通过 lib 级别 使用标准库 API。当参考文件与已安装包的 .d.ts 或 CHANGELOG 不一致时,以已安装版本为准。
| 优先使用 | 而不是 | 自版本 |
|---|---|---|
const config = { ... } satisfies Config | const config: Config(会拓宽)、as Config | 4.9 |
function f<const T>(x: T) | 在每个调用点使用 as const | 5.0 |
使用 verbatimModuleSyntax 的 import type { T } | importsNotUsedAsValues(已移除) | 5.0 |
使用 [Symbol.dispose] 的 using / await using | try / finally 清理 | 5.2, lib esnext |
import data from "./d.json" with { type: "json" } | assert { type: "json" }(自 6.0 起报错) | 5.3 |
NoInfer<T> | T & {} 推理阻塞器 | 5.4 |
推断谓词:xs.filter((x) => x !== undefined) | 手写 (x): x is T => 守卫 | 5.5 |
文档与注释
- 导出的项需要
/** */文档,说明合约:@throws用于抛出的错误,@deprecated指明替代项,当签名不能明显说明用法时提供@example。
依赖
- 重用
package.json中已有的内容,并在平台满足需求时优先使用平台:fetch、structuredClone、crypto.randomUUID、AbortSignal.timeout、node:前缀的内置模块。 - 使用项目的包管理器添加依赖,并将
@types/*固定到运行时包的主版本号(@types/node:固定到engines中最低的 Node 版本)。
参考
加载与你当前所在分支匹配的那个:
references/types.md:泛型、条件类型和映射类型、类型谓词、模块扩充、类型级别的错误。references/errors.md:定义错误类型、结果联合、cause、层之间的错误边界。references/async.md:Promise、使用AbortSignal取消、并发、using、异步迭代。references/modules.md:tsconfig.json、package.json、模块解析、ESM 和 CommonJS、TypeScript 6 和 7 迁移、配置错误。references/zod.md:解析不受信任的输入、模式、Zod 4 API。references/react.md:React 组件、Hooks、React 19 API、React Compiler。references/nextjs.md:Next.js App Router、服务器组件和函数、"use cache"、路由类型。references/recharts.md:Recharts 图表、带类型的dataKey、自定义提示框和形状、图表尺寸。references/lints.md:设置或更改 Biome、oxlint 或 oxfmt。references/testing.md:编写测试、类型测试、React 组件测试、模拟。references/perf.md:类型检查慢、编辑器延迟、打包大小、测量到的热路径。references/api.md:设计或更改已发布包的公共 API、exports、声明文件、semver;将typescript作为库导入的工具。references/runtimes.md:在 Node、Bun 或 Deno 上直接运行.ts文件。references/monorepo.md:pnpm 工作区、Turborepo、相互依赖的包。references/jsdoc.md:使用checkJs检查的.js文件、JSDoc 类型、将 JavaScript 迁移到 TypeScript。