You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 21:32:35