MST

星途 面试题库

面试题:TypeScript 装饰器在 Node.js 企业级后端应用的深度应用

请深入探讨 TypeScript 装饰器在 Node.js 企业级后端应用中的应用场景,如路由处理、日志记录、权限控制等。详细说明如何自定义装饰器,并阐述在使用装饰器时可能遇到的问题及解决方案。
25.1万 热度难度
前端开发TypeScript

知识考点

AI 面试

面试题答案

一键面试

1. TypeScript 装饰器在 Node.js 企业级后端应用中的应用场景

路由处理

在 Node.js 后端应用中,使用 Express 等框架时,装饰器可用于定义路由。例如:

import { Controller, Get, Post } from '@decorators/express';

@Controller('/api')
class MyController {
    @Get('/users')
    getUsers() {
        // 处理获取用户列表逻辑
        return 'User list';
    }

    @Post('/users')
    createUser() {
        // 处理创建用户逻辑
        return 'User created';
    }
}

这样可以将路由定义与业务逻辑紧密结合,使代码结构更清晰。

日志记录

装饰器可以方便地在方法调用前后添加日志记录。比如:

function log(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    const originalMethod = descriptor.value;
    descriptor.value = function (...args: any[]) {
        console.log(`Calling method ${propertyKey} with args:`, args);
        const result = originalMethod.apply(this, args);
        console.log(`Method ${propertyKey} returned:`, result);
        return result;
    };
    return descriptor;
}

class MyService {
    @log
    doSomething() {
        return 'Some result';
    }
}

每次调用 doSomething 方法时,都会记录方法调用信息和返回结果。

权限控制

在企业级应用中,权限控制很重要。可以通过装饰器来检查用户是否有执行某个方法的权限。例如:

function checkPermission(role: string) {
    return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
        const originalMethod = descriptor.value;
        descriptor.value = function (...args: any[]) {
            const userRole = getCurrentUserRole(); // 假设此函数获取当前用户角色
            if (userRole === role) {
                return originalMethod.apply(this, args);
            } else {
                throw new Error('Permission denied');
            }
        };
        return descriptor;
    };
}

class AdminService {
    @checkPermission('admin')
    deleteUser() {
        // 删除用户逻辑
        return 'User deleted';
    }
}

只有具有 admin 角色的用户才能调用 deleteUser 方法。

2. 自定义装饰器

类装饰器

类装饰器作用于类的定义。例如,为类添加元数据:

function addMetadata(meta: any) {
    return function (target: Function) {
        Reflect.defineMetadata('meta', meta, target);
    };
}

@addMetadata({ key: 'value' })
class MyClass {}

const meta = Reflect.getMetadata('meta', MyClass);
console.log(meta); // { key: 'value' }

方法装饰器

方法装饰器作用于类的方法。前面日志记录和权限控制的例子就是方法装饰器。其结构一般为:

function myMethodDecorator(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    // 可以修改方法的行为
    const originalMethod = descriptor.value;
    descriptor.value = function (...args: any[]) {
        // 前置逻辑
        const result = originalMethod.apply(this, args);
        // 后置逻辑
        return result;
    };
    return descriptor;
}

属性装饰器

属性装饰器作用于类的属性。比如,验证属性值:

function validateNumber(target: any, propertyKey: string) {
    let value: number;
    const getter = function () {
        return value;
    };
    const setter = function (newValue: number) {
        if (typeof newValue!== 'number' || newValue < 0) {
            throw new Error('Invalid number');
        }
        value = newValue;
    };
    Object.defineProperty(target, propertyKey, {
        get: getter,
        set: setter,
        enumerable: true,
        configurable: true
    });
}

class MyData {
    @validateNumber
    myNumber: number;
}

const data = new MyData();
data.myNumber = 10; // 正常设置
// data.myNumber = 'not a number'; // 抛出错误

3. 使用装饰器时可能遇到的问题及解决方案

兼容性问题

问题:装饰器是 ES7 提案,在一些旧版本的 Node.js 环境中可能不支持。 解决方案:使用 Babel 进行转码,配置 @babel/plugin-proposal-decorators 插件,将装饰器语法转换为 ES5 兼容的代码。

装饰器执行顺序

问题:多个装饰器应用到同一个目标时,执行顺序可能不符合预期。 解决方案:了解装饰器执行顺序规则。类装饰器在类定义之前执行,方法装饰器、属性装饰器在类定义之后执行。多个装饰器从下往上(靠近目标的先执行)应用。

调试困难

问题:装饰器修改了原有方法或类的行为,调试时难以追踪。 解决方案:在装饰器内部添加详细的日志输出,记录装饰器的执行过程和参数。也可以使用调试工具,如 Node.js 自带的调试器或 IDE 的调试功能,逐步跟踪装饰器逻辑。

性能问题

问题:频繁使用装饰器,尤其是在方法装饰器中添加复杂逻辑,可能影响性能。 解决方案:尽量简化装饰器中的逻辑,避免在装饰器中执行耗时操作。如果需要复杂逻辑,可以考虑将其移到单独的服务中,装饰器只进行简单的调用。