嵌套类结构会导致编译器错误

时间:2021-07-21 16:00:26

标签: typescript

我有一个这样的类层次结构:

用户:

  • 角色:角色[]

角色:

  • 权限:权限

每个类都有一个构造函数,可以将参数转换为指定的类,例如 User 将角色转换为 Role 实例。角色将权限转换为权限实例。

示例代码:

const user = new User({
        roles: [
            {role_name: 'admin', permissions: {'*': '*'}} 
        ]
    })

当我使用嵌套的权限对象为用户传递 JSON 时,我收到编译器错误,因为 Permission 上有方法。 Property 'has' is missing in type '{ '*': string; }' but required in type 'Permission'.

如何避免这些问题?

编辑:

如何让 Roles 类将权限作为对象接受,以便它可以正确地将 JSON 强制转换为 Permission 类型?

// users.ts
export interface User extends BaseModel {
    createdAt?: string;
    updatedAt?: string;
    email?: string;
    username?: string;
    fullname?: string;
    microsoftId?: string;
    roles?: UserRole[];
}

export class User {
    constructor(props?: Partial<User>) {
        if(!props){
            props = {};
        }

        props.roles = props.roles?.map(role => new Role(role)) || [];
        
        Object.assign(this, props);
    }

    hasPermission(service: string, perm: PermissionType){
        for(let role of this.roles || []){
            const has = (role.permissions as Permission).has(service, perm);
            if(has){
                return has;
            }
        }

        return false;
    }
}

// roles.ts


export interface Permission {
    [key: string]: PermissionType[]|any;
}

export class Permission {
    constructor(props: Partial<Permission>){
        Object.assign(this, props);
    }

    has(service: keyof Permission, permName: PermissionType|undefined): boolean {
        //.....
    }
}
export interface Role extends BaseModel {
    createdAt?: string;
    updatedAt?: string;
    role_name?: string;
    description?: string;
    scopes?: string[];
    permissions: Permission;
}
export class Role {
    constructor(props?: Partial<Role>) {
        if(!props){
            props = {};
        }

        props.permissions = new Permission(props.permissions || {});
        
        if (!Array.isArray(props.scopes)) {
            props.scopes = [];
        }

        Object.assign(this, props);
    }
}

1 个答案:

答案 0 :(得分:1)

不要将接口命名为与类相同的名称。您正在合并名称并创建一个坦率而相当尴尬的设置。有一个单独的构造函数参数接口。

export interface PlainPermission {
    [key: string]: PermissionType[]|any;
}

export class Permission implements PlainPermission {
    [key: string]: PermissionType[]|any;

    constructor(props: Partial<PlainPermission>){
        Object.assign(this, props);
    }

    has(service: keyof Permission, permName: PermissionType|undefined): boolean {
        //.....
    }
}

RoleUser 也是如此。然后你就有了明显的区别:PlainPermission 是你传递给构造函数的“普通”Javascript 对象,而 Permission 是附加了所有漂亮、方便的方法的东西。

当您定义一个接口和一个同名的类时,它们会合并以创建一个更大的类,Permission 被定义为“任何具有接口 em> has 方法”,这不是您想要的。

您将不得不从类中的接口中复制字段名称,但这是有道理的:接口是实现某些东西的承诺,而类是实际实现。一个是合同,另一个做合同中规定的工作。您可以使用 implements PlainPermission 作为额外检查,以确保您正确实现了接口。