mcpField
T
typescript

typescript

github.com/epicenterhq开发4.8k124 次安装3 周前更新
代码仓库

关于 typescript

一套面向项目级 TypeScript 开发的编码规范,供开发者在组织类型、API 和实现时参考。它强调复用类型来源、工厂函数、类型安全,以及便于代码导航的导出设计。

主要功能

  1. 01优先推导或导入现有类型
  2. 02使用 type 而非 interface
  3. 03以工厂函数替代类
  4. 04规范类型导出与代码导航
  5. 05强调类型安全和穷尽检查

适用场景

  1. 01统一 TypeScript 项目的编码约定
  2. 02设计可复用的类型和工厂 API
  3. 03检查导出结构与 Go-to-Definition 导航

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/epicenterhq/epicenter --skill typescript

安装后在 Claude Code 中输入/typescript确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
advanced-typescript-features.md
factory-patterns.md
project-conventions.md
readable-constraint-errors.md
runtime-schema-patterns.md
testing-patterns.md
type-organization.md
type-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