MST
星途 面试题库

面试题:TypeScript方法装饰器与依赖注入

假设你正在开发一个大型的前端应用,需要通过TypeScript方法装饰器来实现依赖注入功能。请设计一套方案,使得在类方法执行前,能够自动注入所需的依赖,并处理好依赖之间的生命周期和作用域问题。请详细描述你的设计思路,并给出关键代码示例。
32.4万 热度难度
前端开发TypeScript

知识考点

AI 面试

面试题答案

一键面试

设计思路

  1. 定义依赖注入装饰器:通过装饰器来标记需要注入依赖的方法,并指定依赖的类型或名称。
  2. 管理依赖容器:创建一个容器来存储所有的依赖实例,负责依赖的创建、生命周期管理和作用域控制。
  3. 解析依赖:在方法执行前,从依赖容器中获取所需的依赖实例,并注入到方法的参数列表中。

关键代码示例

  1. 定义依赖注入装饰器
function Injectable() {
    return function (target: any) {
        Reflect.defineMetadata('isInjectable', true, target);
    };
}

function Inject(dependencyKey: string | symbol) {
    return function (target: any, propertyKey: string, parameterIndex: number) {
        let dependencies: { [key: string | symbol]: number[] } = Reflect.getOwnMetadata('dependencies', target, propertyKey) || {};
        if (!dependencies[dependencyKey]) {
            dependencies[dependencyKey] = [];
        }
        dependencies[dependencyKey].push(parameterIndex);
        Reflect.defineMetadata('dependencies', dependencies, target, propertyKey);
    };
}
  1. 依赖容器
class DependencyContainer {
    private instances: { [key: string | symbol]: any } = {};
    private factories: { [key: string | symbol]: () => any } = {};

    register(key: string | symbol, factory: () => any) {
        this.factories[key] = factory;
    }

    get(key: string | symbol): any {
        if (!this.instances[key]) {
            if (!this.factories[key]) {
                throw new Error(`Dependency with key '${key}' not registered.`);
            }
            this.instances[key] = this.factories[key]();
        }
        return this.instances[key];
    }
}
  1. 使用装饰器和依赖容器
@Injectable()
class Logger {
    log(message: string) {
        console.log(`[LOG] ${message}`);
    }
}

@Injectable()
class DataService {
    constructor(private logger: Logger) {}

    getData() {
        this.logger.log('Fetching data...');
        return 'Some data';
    }
}

const container = new DependencyContainer();
container.register('Logger', () => new Logger());
container.register('DataService', () => new DataService(container.get('Logger')));

function resolveDependencies(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    const originalMethod = descriptor.value;
    descriptor.value = function (...args: any[]) {
        const dependencies: { [key: string | symbol]: number[] } = Reflect.getOwnMetadata('dependencies', target, propertyKey) || {};
        Object.keys(dependencies).forEach(key => {
            const paramIndices = dependencies[key];
            paramIndices.forEach(index => {
                args[index] = container.get(key);
            });
        });
        return originalMethod.apply(this, args);
    };
    return descriptor;
}

class App {
    @resolveDependencies
    start(dataService: DataService) {
        const data = dataService.getData();
        console.log('App started with data:', data);
    }
}

const app = new App();
app.start();