MST

星途 面试题库

面试题:TypeScript类装饰器与元数据结合的高级应用

在一个大型企业级应用中,需要对不同类的方法进行权限控制。利用TypeScript类装饰器结合元数据,实现一个系统,能够标记哪些用户角色可以访问类中的哪些方法。例如,有`Admin`、`User`两种角色,`Admin`角色可以访问所有方法,`User`角色只能访问特定标记为可访问的方法。请详细说明设计思路并给出关键的TypeScript代码实现。
34.3万 热度难度
前端开发TypeScript

知识考点

AI 面试

面试题答案

一键面试

设计思路

  1. 定义角色类型:首先定义表示不同用户角色的类型,如AdminUser
  2. 使用元数据:利用TypeScript的reflect - metadata库来存储和读取方法的权限元数据。在类装饰器和方法装饰器中使用元数据来标记方法允许的角色。
  3. 类装饰器:创建一个类装饰器,它将遍历类的所有方法,并使用方法装饰器标记每个方法的权限。
  4. 方法装饰器:定义方法装饰器,用来标记哪些角色可以访问该方法。
  5. 权限验证函数:编写一个函数,在调用方法前验证当前用户角色是否有权限访问该方法。

关键TypeScript代码实现

import 'reflect - metadata';

// 定义角色类型
type Role = 'Admin' | 'User';

// 方法装饰器,用于标记哪些角色可以访问该方法
function allow(roles: Role[]) {
    return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
        Reflect.defineMetadata('allowedRoles', roles, target, propertyKey);
        return descriptor;
    };
}

// 类装饰器,用于为类的所有方法应用权限控制
function applyPermissions(target: Function) {
    const prototype = target.prototype;
    for (const propertyKey in prototype) {
        if (typeof prototype[propertyKey] === 'function') {
            if (!Reflect.hasMetadata('allowedRoles', prototype, propertyKey)) {
                // 如果没有显式标记,则默认只有Admin可以访问
                allow(['Admin'])(prototype, propertyKey, prototype[propertyKey]);
            }
        }
    }
    return target;
}

// 权限验证函数
function checkPermission(target: any, propertyKey: string, role: Role) {
    const allowedRoles = Reflect.getMetadata('allowedRoles', target, propertyKey);
    return allowedRoles.includes(role);
}

// 示例类
@applyPermissions
class MyService {
    @allow(['User', 'Admin'])
    public publicMethod() {
        console.log('This is a public method.');
    }

    private privateMethod() {
        console.log('This is a private method.');
    }
}

// 模拟调用
const myService = new MyService();
const userRole: Role = 'User';
if (checkPermission(myService, 'publicMethod', userRole)) {
    myService.publicMethod();
} else {
    console.log('You do not have permission to access this method.');
}