关于 typescript
一套面向项目级 TypeScript 开发的编码规范,供开发者在组织类型、API 和实现时参考。它强调复用类型来源、工厂函数、类型安全,以及便于代码导航的导出设计。
主要功能
- 01优先推导或导入现有类型
- 02使用 type 而非 interface
- 03以工厂函数替代类
- 04规范类型导出与代码导航
- 05强调类型安全和穷尽检查
适用场景
- 01统一 TypeScript 项目的编码约定
- 02设计可复用的类型和工厂 API
- 03检查导出结构与 Go-to-Definition 导航
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ epicenterhq/ epicenter --skill typescript安装后在 Claude Code 中输入/typescript确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/advanced-typescript-features.mdfactory-patterns.mdproject-conventions.mdreadable-constraint-errors.mdruntime-schema-patterns.mdtesting-patterns.mdtype-organization.mdtype-safety-and-control-flow.md详细介绍
TypeScript 指南
项目范围内的 TypeScript 约定可与 arktype、typebox、testing 和 factory-function-composition 等更具体的技能结合使用。
核心规则
- 声明新的具名类型前,先尝试推导或导入类型。新的具名类型必须有充分理由,成为真正的契约、协议词汇、可区分的结果联合类型、能力端口或多实现形态。
- 将本地复制形状视为边界异味。优先使用所属运行时类型、模式推导、工厂返回类型、函数签名或由调用方负责的能力函数。
- 从具名基类型向上组合类型:使用
Base & Extra扩展,绝不使用Omit<Base, 'k'> & { k: U }先删除再替换。在你编写的类型中出现Omit<...> &,意味着你正在结构性地覆盖类型;这表明较小的基类型应该有自己的名称。要缩窄表面时,返回该基类型,而不是通过Omit删除成员。参见项目约定。 - 使用
type,不要使用interface。 - 除非要与上游类型完全匹配,否则只对数组和映射使用
readonly。 - 在 camelCase 中,将缩写视为普通单词:
parseUrl、defineTable、readJson、customerId。 - 在相对导入中使用
.js扩展名。不要使用省略扩展名或.ts的相对导入。 - 在声明处导出符号。
export { ... } from ...仅用于 barrel 文件。 - 在 React/TSX 中,优先使用带显式 props 参数的具名函数组件,而不是
React.FC/FunctionComponent;只有在将组件值存入注册表或作为数据传递时,才为其值添加类型。 - 优先使用工厂函数而不是类。通过闭包位置体现私有 API 与公共 API 的区别。
- 使用描述性泛型名称,并以
T为前缀,例如TSchema、TDefs和TKey。 - 当对象是配置集合时,在函数签名中解构选项。只有当命名值是正在转换或转发的领域对象时,才保留该值。
- 让 TypeScript 推导私有和内部返回类型。仅在有助于清晰表达或打破循环推导时,为导出的 API 添加类型注解。
- 如果导出的类型与
create*、attach*、open*或类似工厂返回的对象完全一致,则使用ReturnType<typeof createThing>从实现中推导。在工厂后紧接着声明导出的输出类型别名。将输入、配置、数据、协议和多实现契约类型放在工厂之前。 - 将面向使用者的 JSDoc 添加到返回对象的成员上。在返回对象中添加具体成员注解,以保留 IntelliSense、缩窄实现细节或稳定公共方法/属性表面。
- 对于柯里化工厂,从内部返回值推导类型,例如
ReturnType<ReturnType<typeof createThing>>。对于泛型工厂,在需要时实例化typeof,例如ReturnType<typeof openThing<TActions>>。 - 从对象字面量推导类型时,通过 getter 保留有意设计为只读的公共表面。不要仅仅因为具体实现以可写方式存储内部状态,就将其暴露为可写。
- 当身份意味着特定工厂的输出,而非偶然匹配的形状探测时,使用
Symbol品牌。 - 避免使用
as any。改用unknown、验证、品牌或更窄的辅助函数。 - 检查可选属性时,优先使用可选链,而不是
in检查或真值判断。 - 对用于回答问题的布尔值,使用
is、has或can前缀。 - 针对同一值进行重复相等比较时,优先使用
switch而不是if/else。需要穷尽性检查时,使用default: value satisfies never。 - 对有限值映射,优先使用
Record查找表,而不是嵌套三元表达式。 - 自底向上组合类型化错误。不要在边界处过滤宽泛的上游错误联合类型。有关 Result 传播、恢复和呈现,请参见
error-handling。 - 质疑那些掩盖无效状态的静默回退。解析和序列化时保留往返不变性。
感知转到定义
整理类型和导出时,务必考虑「转到定义」。开发者在调用点按下「转到定义」时,应尽可能跳转到实际的真实来源。如果某种设计选择会多增加一次导航跳转,就必须有其他方面的价值(例如,真正的验证边界、已发布的契约或多实现端口)。
需要留意的具体退化情况:
- 解构并重新导出模块级对象:
const stub = { fn, gn } satisfies T; export const { fn, gn } = stub;会让「转到定义」跳转到解构所在行,而非真正的定义。优先采用逐项导出的satisfies,或直接使用export const fn = ... satisfies T['fn']。 - 用
typeof Real注解替代satisfies:export const fn: typeof Real = unreachable会让导航无法识别底层值的身份。export const fn = unreachable satisfies typeof Real则能保留该值作为真实来源。 - 多实现端口使用
: T注解而非satisfies:这是上一条规则的补充。当接口T有多个实现(#platform/*或 browser/tauri 分流),且某个实现有意比T更窄(例如,忽略了契约中声明的某个参数)时,export const x = {...} satisfies T会泄露这个较窄的具体类型,导致调用方看到的方法类型因平台而异。使用export const x: T = {...}来发布宽泛契约,较窄的实现仍然可以通过类型检查。参考:whispering 的ManualRecorderLive: RecorderService<...>(二元契约背后的单元 CPAL 实现)。 - 非 barrel 文件中的重新导出链:在
index.ts以外使用export { X } from './alias'会多增加一次跳转,却没有任何收益。仅在 barrel 文件中使用export { ... } from ...;其他地方应在声明处导出。 - 不改变行为的适配器/代理/包装器:
fromX转换器或薄封装会让「转到定义」跳转到包装器。应改为扩展底层工厂的返回形状(参见factory-function-composition中的 “collapsed adapter” 规则)。 - 手动返回类型注解重复声明 zone 4:用手写接口为工厂添加注解,会让「转到定义」跳转到别名。让工厂返回其具体对象,然后紧接着在其后定义导出的别名:
export type Thing = ReturnType<typeof createThing>。这样导航会停留在返回对象的成员上,并由这些成员的 JSDoc 提供公开文档。若涉及悬停或导航问题,请参阅工厂方法文档。 - 冗长的
satisfies泛型列表:如果返回对象应证明它符合某个泛型契约,但satisfies Contract<A, B, C> & Extras会迫使调用方重复声明推断出的表、操作或运行时类型,应优先使用由契约模块提供的受约束恒等辅助函数。例如:return defineWorkspace({ ...workspace, ...runtime }),其中辅助函数接受TWorkspace extends Workspace<...>并返回TWorkspace。这样可以让调用点保持简洁、保留精确推断的返回类型,并让「转到定义」跳转到实际对象成员。 - 何时不应添加
defineX:不要仅为给简单的satisfies检查起个辅助函数名,就将其包装起来。如果契约没有必需的类型参数,或其泛型有默认值,使得satisfies Contract易于阅读,应优先使用satisfies。只有当辅助函数能减少读者原本必须处理的泛型冗余时,额外的名称才有价值。
如需了解影响跨包导航的更广泛公开形状决策,请参阅 rethink。
参考索引
- 项目约定:派生类型、本地形状副本、导入、barrel、工厂、泛型、解构和工厂返回类型的详细示例。
- 类型安全与控制流:身份品牌、类型断言、可选属性、布尔值命名、switch 语句、记录查找、错误组合、回退异味和往返不变量。
- 类型组织:
types.ts的位置、共置规则、内联与提取的跳转测试、选项和 ID 命名。 - 工厂模式:以工厂为重点的重构、参数解构和耦合状态提取。
- 运行时 Schema 模式:arktype、品牌 ID、可选属性语法和工作区表 ID。
- 测试模式:内联的单次使用设置和源遮蔽测试。
- TypeScript 高级特性:迭代器辅助方法和 const 泛型数组推断。
- 易读的约束错误:当字面量键或字符串约束需要在编译器诊断中指出有问题的值时,请阅读此文档。
评论
支持 Markdown