如何在TypeScript单一Builder模式中实现创建/更新的条件运行时验证
Permission Builder重构方案(Node.js/TypeScript)
针对创建与更新操作的互斥约束,拆分出独立的buildForCreate()和buildForUpdate()方法是最直接且易维护的方案,避免单个build()方法中复杂的条件分支,同时让调用方明确当前操作类型,减少误操作。
重构后代码实现
import { CustomError } from "../../../shared"; export class PermissionBuilder { private id?: string; private name?: string; private module?: string; constructor() {} setId(id: string): PermissionBuilder { if (!id || !id.trim()) throw CustomError.badRequest('ID cannot be empty'); this.id = id; return this; } setName(name: string): PermissionBuilder { if (!name || !name.trim()) throw CustomError.badRequest('Name cannot be empty'); this.name = name; return this; } setModule(module: string): PermissionBuilder { if (!module || !module.trim()) throw CustomError.badRequest('Module cannot be empty'); this.module = module; return this; } // 构建创建用Permission实例 buildForCreate() { // 创建操作:禁止传入ID if (this.id !== undefined) { throw CustomError.badRequest('Create operation cannot specify ID'); } // 创建操作:name和module为必填项 if (!this.name || !this.name.trim()) { throw CustomError.badRequest('Name is required for create operation'); } if (!this.module || !this.module.trim()) { throw CustomError.badRequest('Module is required for create operation'); } return { id: this.id, name: this.name, module: this.module, }; } // 构建更新用Permission实例 buildForUpdate() { // 更新操作:ID为必填项 if (!this.id || !this.id.trim()) { throw CustomError.badRequest('ID is required for update operation'); } // 更新操作:至少提供name或module其中一个 const hasValidName = this.name && this.name.trim(); const hasValidModule = this.module && this.module.trim(); if (!hasValidName && !hasValidModule) { throw CustomError.badRequest('At least one of name or module must be provided for update'); } return { id: this.id, name: hasValidName ? this.name : undefined, module: hasValidModule ? this.module : undefined, }; } }
核心逻辑说明
- 操作类型明确区分:调用方必须调用对应操作的build方法,从根源上避免混淆创建/更新逻辑。
- 创建操作验证:
- 强制
id必须为undefined,防止创建时传入数据库自增ID或无效ID。 - 校验
name和module必须非空,确保创建的实例具备必要字段。
- 强制
- 更新操作验证:
- 强制
id必须存在且非空,确保更新目标明确。 - 校验
name和module至少有一个有效字段,避免无意义的空更新。 - 未提供的字段保持
undefined,仓储层可通过判断undefined来跳过对应列的更新操作。
- 强制
额外优化建议(可选)
如果需要在TypeScript层面强化类型约束,可以为创建和更新分别定义不同的返回类型:
type CreatePermission = { id?: never; name: string; module: string; }; type UpdatePermission = { id: string; name?: string; module?: string; }; // 对应修改build方法的返回类型 buildForCreate(): CreatePermission { /* ... */ } buildForUpdate(): UpdatePermission { /* ... */ }
这样调用方在编译阶段就能发现错误(比如创建时传入id),进一步提升代码可靠性。
内容的提问来源于stack exchange,提问作者JoseYC
相关产品推荐
相关产品推荐

