TypeScript 开发规则
前置条件检测
在应用项目约定之前,先检查 tsconfig、运行时/框架配置、lint/格式化配置、路径别名、package 脚本以及有代表性的模块。仅当配置或既有模式支持某条规则时,才将其视为项目特有规则。将基于有限样本得出的结论标注为推断所得。当相互竞争的约定会改变公共契约、运行时行为或错误边界时,应停下来指出所需的约束来源或用户决策。
后端实现中的类型安全
数据流中的类型安全
输入层(unknown)-> 类型守卫 -> 业务层(类型已保证)-> 输出层(序列化)
后端特有的类型场景:
- API 通信:将响应作为
unknown接收,并通过类型守卫进行校验 - 表单输入:外部输入作为
unknown,校验后再确定类型 - 遗留系统集成:将遗留边界作为
unknown接收;将任何有依据支撑的类型断言隔离在拥有该边界的适配器内 - 测试代码:使用配置好的测试工具定义模拟输入/输出类型;对有意为之的部分 fixture 使用
Partial<T>,仅在配置了 Vitest 时使用带类型的vi.fn<[Args], Return>()
编码约定
类的使用标准
- 推荐:使用函数与接口实现
- 理由:提升可测试性与函数组合的灵活性
- 允许使用类的情形:
- 框架要求(NestJS Controller/Service、TypeORM Entity 等)
- 自定义错误类定义
- 状态与业务逻辑紧密耦合时(例如 ShoppingCart、Session、StateMachine)
- 判断标准:若“这份数据是否具有行为?”的答案是肯定的,可考虑使用类
typescript
// 函数与接口 interface UserService { create(data: UserData): User } const userService: UserService = { create: (data) => {...} }
函数设计
- 最多 0-2 个参数:3 个及以上参数时使用对象
typescript
// 对象参数 function createUser({ name, email, role }: CreateUserParams) {}
依赖注入
- 将外部依赖作为参数注入:确保可测试性与模块化
typescript
// 将依赖作为参数接收 function createService(repository: Repository) { return {...} }
异步处理
- Promise 处理:遵循仓库既有风格;当能明确表达执行顺序与错误传播时使用
async/await - 错误处理:当当前层能够转换、丰富、恢复或记录该失败时添加
try-catch。否则应让 Promise 拒绝传播到拥有该边界的位置 - 类型定义:明确定义返回值类型(例如
Promise<Result>)
格式规则
- 遵循仓库配置的格式化工具,包括其分号策略
- 类型使用
PascalCase,变量/函数使用camelCase - 仅通过
tsconfig或所配置构建工具中声明的别名使用绝对导入;否则使用相对导入
整洁代码原则
- 在当前改动范围内移除未使用的代码
- 删除调试用的
console.log() - 保持可执行源码中不含被注释掉的代码;版本控制系统会保留被移除的实现
- 注释解释“为什么”(而非“做了什么”)
错误处理
错误结果规则:每个失败都应有唯一的责任归属结果:返回一个带类型的预期错误、按明确规定的需求恢复,或携带诊断上下文向外传播。在拥有可观测性职责的边界处记录日志,以避免同一失败被重复记录。
快速失败原则:出错时应快速失败,以防止在无效状态下继续处理
typescript// 无效做法:回退值掩盖了调用方所需的失败信息 catch (error) { return defaultValue // 掩盖了错误 } // 明确传播并附加上下文 catch (error) { throw new Error('Processing failed', { cause: error }) }
Result 类型模式:用类型表达错误以实现显式处理
typescripttype Result<T, E> = { ok: true; value: T } | { ok: false; error: E } // 示例:用类型表达错误的可能性 function parseUser(data: unknown): Result<User, ValidationError> { if (!isValid(data)) return { ok: false, error: new ValidationError() } return { ok: true, value: data as User } }
自定义错误类
typescriptexport class AppError extends Error { constructor(message: string, public readonly code: string, public readonly statusCode = 500) { super(message) this.name = this.constructor.name } } // 按用途区分:ValidationError(400)、BusinessRuleError(400)、DatabaseError(500)、ExternalServiceError(502)
各层特有的错误处理(后端)
- API 层:转换为 HTTP 响应,输出日志时排除敏感信息
- Service 层:检测业务规则违反,原样传播 AppError
- 仓储层:将技术错误转换为领域错误
结构化日志与敏感信息保护 仅记录当前信任边界所允许的字段。记录日志前应对凭证、令牌、密钥、支付数据和个人数据进行脱敏处理。
异步错误处理
- 当运行时暴露
unhandledRejection/uncaughtException事件时,在应用入口处配置运行时级别的处理;库应将进程级策略留给宿主决定 - 在能够添加带类型结果、恢复逻辑或诊断上下文的那一层捕获异步失败
- 除非某项明确规定的需求要求在该层恢复,否则应在补充信息后继续传播失败
性能优化
- 流式处理:当实测的输入规模可能超出可用内存预算,或需求要求增量输出时,使用流式处理或有限批次;记录触发该判断的度量数据或约束条件
- 资源生命周期:在拥有其生命周期的边界处释放定时器、订阅、句柄及被持有的引用


