TypeScript如何编写类型安全的实体包装类且无需复制属性?
TypeScript 类型安全的实体包装类实现(无需显式复制属性)
在TypeScript里,确实有几种类型安全的方式实现包装类,不用手动复制每个DTO的属性,完美符合DRY原则,下面是具体方案:
方案一:用 Object.assign 自动合并属性
这种方式最直观,在构造函数里把DTO的所有属性直接合并到类实例上,同时让类实现DTO接口,保证类型校验:
interface UserDto { readonly id: string; readonly friends: string[]; } class User implements UserDto { // 声明DTO的属性,让TS识别类型 readonly id: string; readonly friends: string[]; constructor(dto: UserDto) { // 自动合并所有属性,不用手动赋值 Object.assign(this, dto); } // 添加自定义方法 friendsCount() { return this.friends.length; } } // 使用示例 const userDto: UserDto = { id: "1", friends: ["2", "3"] }; const user = new User(userDto); console.log(user.id); // 输出 "1" console.log(user.friendsCount()); // 输出 2
优点:代码简洁,类型安全,编译器会自动校验DTO和类的属性是否匹配,不用手动写一堆赋值语句。
方案二:原型继承改造DTO
直接把DTO对象的原型指向包装类的原型,让DTO能直接调用自定义方法,不用创建额外实例:
interface UserDto { readonly id: string; readonly friends: string[]; } class User { friendsCount() { // 类型断言让TS识别DTO属性 return (this as UserDto).friends.length; } } // 工厂函数处理原型并返回正确类型 function createUser(dto: UserDto): User & UserDto { Object.setPrototypeOf(dto, User.prototype); return dto as User & UserDto; } // 使用示例 const userDto: UserDto = { id: "1", friends: ["2", "3"] }; const user = createUser(userDto); console.log(user.id); // 输出 "1" console.log(user.friendsCount()); // 输出 2
优点:轻量化,没有额外的类实例开销;缺点是如果DTO被冻结(Object.freeze),setPrototypeOf会失效,而且类型上需要用交叉类型合并。
方案三:用 Proxy 代理DTO
通过Proxy包装DTO,拦截属性访问,既能直接访问DTO的属性,又能调用自定义方法,灵活性最高:
interface UserDto { readonly id: string; readonly friends: string[]; } class UserWrapper { constructor(private dto: UserDto) {} // 自定义方法 friendsCount() { return this.dto.friends.length; } // 处理属性访问逻辑 get(prop: keyof UserDto | string) { if (prop in this) { return (this as any)[prop]; } return this.dto[prop as keyof UserDto]; } } // 创建代理实例,返回合并后的类型 function createUser(dto: UserDto): UserWrapper & UserDto { return new Proxy(new UserWrapper(dto), { get: (target, prop) => target.get(prop as string) }) as UserWrapper & UserDto; } // 使用示例 const userDto: UserDto = { id: "1", friends: ["2", "3"] }; const user = createUser(userDto); console.log(user.id); // 输出 "1" console.log(user.friendsCount()); // 输出 2
优点:可以在Proxy里添加额外逻辑(比如属性权限控制、格式转换),完全隔离DTO和包装类;缺点是相对复杂一点,适合有特殊需求的场景。
内容的提问来源于stack exchange,提问作者Bosse
相关产品推荐
相关产品推荐

