MST

星途 面试题库

面试题:JavaScript复杂函数类型检查与元编程

设计一个JavaScript函数装饰器,该装饰器可以应用于任何函数,使得被装饰的函数在调用前,对所有传入的实参进行类型检查。类型信息通过函数的元数据(可以自定义一种方式来添加元数据,比如使用Symbol)来指定。如果实参类型不匹配,阻止函数执行并抛出详细的类型错误信息。同时要考虑如何处理函数重载(即同一函数名不同参数列表)情况下的类型检查。
18.2万 热度难度
编程语言JavaScript

知识考点

AI 面试

面试题答案

一键面试
// 自定义Symbol用于存储类型元数据
const typeMetadataSymbol = Symbol('typeMetadata');

// 定义装饰器函数
function typeCheckDecorator(target, propertyKey, descriptor) {
    const originalFunction = descriptor.value;

    // 获取类型元数据
    const typeMetadata = Reflect.getOwnMetadata(typeMetadataSymbol, target, propertyKey);

    // 创建新的函数,进行类型检查
    descriptor.value = function (...args) {
        if (!typeMetadata) {
            throw new Error('No type metadata defined for this function.');
        }

        if (args.length!== typeMetadata.length) {
            throw new Error(`Expected ${typeMetadata.length} arguments, but got ${args.length}`);
        }

        for (let i = 0; i < args.length; i++) {
            const arg = args[i];
            const expectedType = typeMetadata[i];
            if (typeof arg!== expectedType) {
                throw new Error(`Argument at index ${i} should be of type ${expectedType}, but got ${typeof arg}`);
            }
        }

        return originalFunction.apply(this, args);
    };

    return descriptor;
}

// 示例函数
class ExampleClass {
    @typeCheckDecorator
    addNumbers(a, b) {
        return a + b;
    }
}

// 设置类型元数据
Reflect.defineMetadata(typeMetadataSymbol, ['number', 'number'], ExampleClass.prototype, 'addNumbers');

// 测试
const example = new ExampleClass();
try {
    console.log(example.addNumbers(1, 2));
    console.log(example.addNumbers('1', 2)); // 这会抛出类型错误
} catch (error) {
    console.error(error.message);
}

// 处理函数重载
class OverloadedClass {
    @typeCheckDecorator
    processInput(input) {
        return `Processed string: ${input}`;
    }

    @typeCheckDecorator
    processInput(input) {
        return `Processed number: ${input}`;
    }
}

// 设置不同重载的类型元数据
Reflect.defineMetadata(typeMetadataSymbol, ['string'], OverloadedClass.prototype, 'processInput', 0);
Reflect.defineMetadata(typeMetadataSymbol, ['number'], OverloadedClass.prototype, 'processInput', 1);

// 测试重载
const overloaded = new OverloadedClass();
try {
    console.log(overloaded.processInput('test'));
    console.log(overloaded.processInput(123));
} catch (error) {
    console.error(error.message);
}

在上述代码中:

  1. 定义了一个typeMetadataSymbol用于存储函数的类型元数据。
  2. typeCheckDecorator装饰器函数获取函数的类型元数据,并在调用原函数前检查传入参数的类型。
  3. 对于函数重载的情况,通过Reflect.defineMetadata的第四个参数(属性描述符索引)来区分不同的重载版本,并分别设置其类型元数据。