typescript

typescript

热门

LobeHub 的 TypeScript 代码规范与类型安全指南。在编辑或修改 TS/TSX/MTS 文件、修复类型报错、决策 interface 与 type 的选型、避免使用 any/object、规范 import type、处理异步流程或使用 ts-expect-error 时使用。

8.1万Star
1.6万Fork
更新于 2026/7/30
SKILL.md
只读
名称
typescript
描述

LobeHub 的 TypeScript 代码规范与类型安全指南。在编辑或修改 TS/TSX/MTS 文件、修复类型报错、决策 interface 与 type 的选型、避免使用 any/object、规范 import type、处理异步流程或使用 ts-expect-error 时使用。

TypeScript 代码风格指南

类型与类型安全

  • 当 TypeScript 能够自动推导类型时,避免手写显式类型注解
  • 避免隐式 any;必要时必须进行显式类型标注
  • 使用更精确的类型:优先使用 Record<PropertyKey, unknown>,避免使用 objectany
  • 定义对象结构(如 React props)时优先使用 interface;联合类型(unions)与交叉类型(intersections)使用 type
  • 优先使用 as const satisfies XyzInterface,而非单纯的 as const
  • 优先使用 @ts-expect-error,其次为 @ts-ignore,最后才考虑 as any
  • 避免设计没有实际意义的 null/undefined 参数;必须设计严格的函数契约(contract)
  • 优先使用 ES 模块扩展(declare module '...')而非 namespace;严禁引入基于 namespace 的扩展模式
  • 当类型需要扩展能力时,在源码类型处暴露一个轻量且可合并的 interface,让各个功能模块/插件在本地自行扩展,而不是把所有扩展字段全集中拼装在一个注册表中
  • 对于包内本地扩展模式(如 PipelineContext.metadata),在读写该字段的处理器(processor)、提供者(provider)或插件(plugin)同级定义对应的 metadata 字段

异步范式

  • 优先使用 async/await,避免使用回调函数(callbacks)或 .then() 链式调用
  • IO 操作坚持异步优先(Async-first):新增的 IO 代码(如 fs、child_process 等)在其边界处必须使用异步 API —— 优先选用基于 Promise 的变体,如 import { readFile } from 'fs/promises',默认严禁使用 *Sync。函数染色(Function coloring)具有不对称性:我们几乎不需要将 async 迁移为 sync,但如果把 sync 改写为 async(当 IO 变慢、引入并发、或衍生出子进程/网络请求时),会导致整条调用链上层的所有调用方全被强制重写 —— 这是会不断滚雪球的同步优先技术债。异步带来的微小开销(如线程池调度、缓存竞态)不能作为使用同步 API 的借口:竞态问题应当通过缓存 Promise 本身而非缓存最终结果来解决
  • *Sync 仅允许在唯一一种场景下使用:即受限于你无法控制的同步契约内部调用点 —— 比如现有的同步签名调用链(在修 bug 时不要为了重构而重构传毒式的旧同步链,但新建的独立模块绝不能继续延续这种同步链),或者像 process.on('exit') 这类纯同步回调。模块加载阶段与 CLI 启动初始化并不是例外场景 —— 在这些地方请直接使用顶层 await(ESM)
  • 在确保安全的前提下,使用 Promise.allPromise.race 进行并发操作

模块导入(Imports)

  • 本项目配置了 simple-import-sort/importsconsistent-type-importsfixStyle: 'separate-type-imports'

  • 独立类型导入:仅导入类型时,一律使用 import type { ... },严禁使用 import { type ... } 内联语法

  • 当一个文件已经包含从某个 npm 包中 import type { ... } 的声明,而你需要补充导入该包的值(value)时,必须拆分为两条独立的语句

    import type { ChatTopicBotContext } from '@lobechat/types';
    import { RequestTrigger } from '@lobechat/types';
    
  • 在每条 import 语句内部,被导入的标识符(specifier)须按字母顺序排序

代码结构

  • 优先使用对象解构
  • 使用一致且具备自我描述力的命名;避免使用晦涩难懂的缩写
  • 用命名清晰的常量替换魔数(magic numbers)和硬编码字符串
  • 代码格式化完全交由自动化工具(Linter / Formatter)处理
  • 优先选择**命名导出(named exports)**而非 export default —— 这可以保持重构重命名与 IDE 自动导入实时同步,避免使用 import Foo from './foo' 时随意改名导致的标识符漂移。仅在框架强制要求时才使用 export default(如 Next.js 的 page/route/layout、React.lazy 加载目标、或 vitest.config.ts 等配置文件)。代码库中目前仍存留不少 export default,那是历史遗留技术债,绝非推荐范式;在上述框架必需场景之外,新写代码切勿效仿既有的 export default
  • 在编写用于通用校验/解析/规范化的本地工具函数(如 record 判定、字符串提取、空串处理、计时辅助工具、JSON 安全处理工具等)之前,请先在 packages/utils 中全局搜索。如果该工具函数已存在或明显属于该包,请直接从 @lobechat/utils(或对应的 @lobechat/utils/* 子路径)导入,切忌在各业务文件中重复实现微型 helper

UI 与主题

  • 优先使用 @lobehub/ui 和 Ant Design 组件,避免直接使用原生 HTML 标签
  • 必须原生支持暗黑模式(Dark mode)与移动端响应式适配
  • 使用 antd-style 的 Token 系统,严禁硬编码颜色值

性能优化

  • 数据库查询时仅选取必要的字段/列

可复用性

  • 复用 packages/utils 或已安装 npm 包中的现有工具
  • 不要手动手写可复用的 record/object-map 类型检查(例如 typeof value === 'object' && value !== null);请直接从 @lobechat/utils/object 导入 isRecordisPlainRecordisObjectLiketoRecordpickStringUnknownRecord 等封装好的 helper
  • Date.now() 的结果保存到常量中重复使用,确保单次操作中的时间戳一致

日志规范

  • 严禁打印用户隐私敏感信息(如 API Key 等)
  • 禁止直接使用 import { log } from 'debug'(会直接输出到 Console)
  • 在 catch 块中统一使用 console.error,而非 debug 软件包
  • .catch() 回调中必须记录错误日志 —— 静默的 .catch(() => fallback) 会吞掉异常导致无法排查问题