MST

星途 面试题库

面试题:TypeScript 复杂场景下回调函数可选参数的类型推断与优化

假设有一个大型的前端项目,其中有一系列相互关联的异步操作。定义一个 `CompositeOperation` 函数,它接受多个回调函数组成的数组作为参数。每个回调函数的第一个参数类型根据前一个回调函数的返回值类型动态推断,第二个参数是可选的错误参数。例如,第一个回调返回 `{id: number}`,则第二个回调的第一个参数类型就是 `{id: number}`。请实现 `CompositeOperation` 函数,确保类型安全和高效,并处理可能出现的类型循环依赖问题。要求详细说明实现思路和关键步骤。
47.9万 热度难度
前端开发TypeScript

知识考点

AI 面试

面试题答案

一键面试

实现思路

  1. 类型推断:利用 TypeScript 的泛型和类型推断机制,使得每个回调函数的第一个参数类型能根据前一个回调函数的返回值类型动态确定。
  2. 错误处理:在每个回调函数中处理可能出现的错误,并将错误传递给后续回调函数。
  3. 循环依赖处理:通过在运行时记录已经处理过的回调函数,避免类型循环依赖导致的无限递归。

关键步骤

  1. 定义泛型:使用泛型来表示回调函数的参数和返回值类型。
  2. 遍历回调数组:按顺序依次执行每个回调函数。
  3. 类型检查与错误处理:在执行回调函数时,检查是否有错误,并根据前一个回调的返回值类型动态推断当前回调的参数类型。
  4. 循环依赖检测:维护一个已执行回调的集合,在执行每个回调前检查是否已执行过,避免循环依赖。

代码实现

function CompositeOperation<TCallbacks extends ((arg: any, err?: Error) => any)[]>(callbacks: TCallbacks): Promise<any> {
    return new Promise((resolve, reject) => {
        const executedCallbacks = new Set();
        let currentIndex = 0;
        let prevResult: any;

        function executeNext() {
            if (currentIndex >= callbacks.length) {
                resolve(prevResult);
                return;
            }

            const callback = callbacks[currentIndex];
            const callbackKey = callback.toString();

            if (executedCallbacks.has(callbackKey)) {
                reject(new Error('循环依赖检测到'));
                return;
            }

            executedCallbacks.add(callbackKey);

            try {
                callback(prevResult, (err: Error) => {
                    if (err) {
                        reject(err);
                    } else {
                        prevResult = callback(prevResult);
                        currentIndex++;
                        executeNext();
                    }
                });
            } catch (error) {
                reject(error);
            }
        }

        executeNext();
    });
}

调用示例

const operation = CompositeOperation([
    (arg, err) => {
        if (err) {
            console.error(err);
        }
        return {id: 1};
    },
    (arg, err) => {
        if (err) {
            console.error(err);
        }
        return arg.id + 1;
    }
]);

operation.then(result => {
    console.log(result);
}).catch(error => {
    console.error(error);
});