SuperAPI AI 工具集

typescript-strict

编程与开发

查看项目

更新于 10-16 11:29

技能介绍

用途

本技能用于在编写 TypeScript 代码、定义类型或 schema、审查类型安全时提供严格模式下的编码规范。

核心规则

  1. 在不可信边界使用 unknown。 将无法避免的 any 限制在外部声明或互操作垫片中,并加以说明。
  2. 无正当理由不得使用类型断言(as Type)。
  3. 遵循代码库的 type/interface 约定。 无既有约定时,根据语言语义选择。
  4. 对已有归属的值集合应派生,绝不重复书写。 在编写字面量联合、枚举列表或对固定值的 ===/|| 链之前,先在代码库中搜索该集合——如果已有 type、as const 列表或 schema 声明了它,就从那个唯一归属派生。

Type 与 Interface

  • type:适用于联合、元组、映射类型和封闭别名,例如带 readonly 字段的 User 类型。类型别名不能通过声明合并重新打开。
  • interface:适用于可扩展的对象契约,如 UserRepository。接口描述对象形状,支持 implements、带冲突检查的继承以及声明合并,适合行为契约和有意开放的扩展点,但数据形状也并非禁止使用。

Schema 重复

每个归属的契约、版本和有界上下文只定义一次 schema,并在该边界内导入。不要仅因字段恰好匹配就把独立部署的消费方或上下文耦合起来;在每个信任边界进行验证和转换,在漂移有影响处使用契约测试。在同一边界内,这是一次搜索而非判断题:当编译期类型和运行时 schema 都需要同一固定值集合时,由一个 as const 列表拥有它,两者都从该列表派生。如果该集合已声明为手写联合,则扩展该声明并从中派生,不要在新 schema 中留下第二份成员副本。

Schema 的归属位置:在信任边界(HTTP 处理器、队列消费者、文件/环境解析、第三方 API 响应)进行验证,然后在内部逻辑中传递派生的普通类型——内部函数信任其输入。建议使用实现 Standard Schema 的 schema 库(Zod 4+、Valibot、ArkType),以保持验证工具的可互换性。

严格模式配置

tsconfig.json 设置包括:strict、noImplicitAny、strictNullChecks、noUnusedLocals、noUnusedParameters、noImplicitReturns、noFallthroughCasesInSwitch、noUncheckedIndexedAccess、exactOptionalPropertyTypes、noPropertyAccessFromIndexSignature、forceConsistentCasingInFileNames、allowUnusedLabels。

各设置含义:

  • strict: true 启用严格类型检查族,包括 noImplicitAny 和 strictNullChecks。
  • noUnusedLocals:未使用的局部变量报错。
  • noUnusedParameters:未使用的函数参数报错。
  • noImplicitReturns:并非所有代码路径都返回值时报错。
  • noFallthroughCasesInSwitch:switch 语句中的贯穿分支报错。
  • noUncheckedIndexedAccess:数组/对象访问返回 T | undefined,防止因假定元素存在而引发运行时错误。
  • exactOptionalPropertyTypes:区分 property?: T 与 property: T | undefined,类型更精确。
  • noPropertyAccessFromIndexSignature:索引签名属性需用括号表示法,强制感知动态访问。
  • forceConsistentCasingInFileNames:防止跨操作系统的大小写敏感问题。
  • allowUnusedLabels:未使用的标签报错,捕获无实际作用的误加标签。

附加规则

  • 当上游类型缺陷暂时无法修复时,优先使用有正当理由的窄范围 @ts-expect-error 而非 @ts-ignore。
  • 对测试同样应用代码库的类型安全策略;使用专门的测试专用互操作垫片,而非削弱全局配置。

架构洞察:noUnusedParameters 可揭示设计问题——带有未使用参数的函数通常表明该参数属于不同层级,严格模式可及早捕获这类设计问题。

不可变性与纯函数

关于不可变性(readonly、ReadonlyArray)、纯函数、组合、Result 类型、数组方法和工厂函数的详细模式,参见 functional 技能。TypeScript 相关要点:在不可变性属于契约一部分时使用 readonly 和 ReadonlyArray<T>,尤其是共享的领域值;编译器执行浅层属性不可变性,不会让嵌套运行时值深度不可变;根据不变量、生命周期和项目约定选择工厂或类,依赖注入不需要二者中任一形式。

信任边界的 Schema-First

当不可信数据跨越边界且程序必须在使用前验证其形状或约束时,需要运行时 schema。常见信号:HTTP、队列、文件、环境或第三方数据进入系统;独立部署系统间交换的数据契约;复用现有生产 schema 能增加有用证据的契约形测试夹具。内部不变量默认不需要 schema;当值在单一可信进程内创建和消费时,智能构造器、品牌类型或领域值对象可能是更清晰的归属。

不需要 schema 的情况:纯内部类型(工具、状态)、Result/Option 类型(无需验证)、TypeScript 工具类型(Partial<T>、Pick<T> 等)、行为契约(接口——结构性而非验证性)、组件 props(除非来自 URL/API)。

品牌类型

当同一底层类型的两个值可以互相替代而编译器不会察觉时,应对原始类型加品牌——两种标识符、金额与费率、原始字符串与规范化字符串。仅在边界验证每个值本身还不够:如果解析后的值仍是 string,交换的一对在所有下游都能编译。为每种类型赋予自己的品牌,仅在值被验证处授予品牌,并以品牌而非原始类型声明消费函数。永远不要在应用代码中散布 as UserId——断言只存在于构造器(或 schema 的 transform)内部,这样每个品牌值都已通过验证。示例中的支付边界接受已四舍五入的整数最小单位,因此拒绝 NaN、无穷大和二进制浮点小数。如果边界改为接受十进制主单位文本,请使用该货币的最小单位指数和具名舍入策略解析(或拒绝多余精度),绝不使用 Math.round(rawNumber * 100)。

总结清单

编写 TypeScript 代码时验证:无未说明的 any,不可避免的互操作是窄范围且被隔离的;无无正当理由的类型断言;type 与 interface 遵循代码库约定或所需语言语义;schema 每个契约/版本/上下文只有一个归属,独立边界不因便利而耦合;代码库已声明的固定值集合应派生而非重复书写;可互换的同类型原始值(标识符、金额、代码)已加品牌,每个品牌仅在值被验证处授予;已启用 strict,附加编译器检查遵循代码库采纳的策略;关于不可变性、纯函数、组合参见 functional 技能。