You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用泛型与映射类型捕获TypeScript深度嵌套上下文值?

解决TypeScript嵌套RunConfig中子级context类型推断为any的问题

你的代码里子级fn的context类型被推断为any,核心原因是TChildren泛型定义为Record<string, RunConfig<any, any>>时,any直接切断了TypeScript的递归类型推断链条,导致嵌套的RunConfig类型信息完全丢失。下面分两步解决问题:先修复子级自身context的类型推断,再实现子级继承祖先上下文的进阶需求。

第一步:修复子级自身context的类型推断

重新定义递归的RunConfig类型,移除会中断推断的any,让TypeScript能递归解析嵌套结构的类型:

type RunConfig<
    TContext extends Record<string, any>,
    TChildren extends Record<string, RunConfig<any, any>> = {}
> = {
    context: TContext;
    fn: (arg: { context: TContext }) => any;
    children: TChildren;
};

function run<
    TContext extends Record<string, any>,
    TChildren extends Record<string, RunConfig<any, any>>
>(config: RunConfig<TContext, TChildren>) {
    const { context, fn, children } = config;
    fn({ context });
    for (const childConfig of Object.values(children)) {
        run(childConfig);
    }
}

// 测试:子级fn的context类型现在能正确推断
run({
    context: { key0: "value0" },
    fn({ context }) {
        // 类型:{ key0: string; }
        console.log("root", { context });
    },
    children: {
        child1: {
            context: { key1: "value1" },
            fn({ context }) {
                // 类型:{ key1: string; }
                console.log("child1", { context });
            },
            children: {
                child11: {
                    context: { key11: "value11" },
                    fn({ context }) {
                        // 类型:{ key11: string; }
                        console.log("child11", { context });
                    },
                    children: {},
                },
            },
        },
        child2: {
            context: { key2: "value2" },
            fn({ context }) {
                // 类型:{ key2: string; }
                console.log("child2", { context });
            },
            children: {},
        },
    },
});

第二步:让子级接收自身+祖先的合并上下文

如果需要子级fn能访问自身和所有祖先的上下文,需要通过递归类型合并上下文,并在run函数中传递合并后的上下文:

// 合并两个对象类型并扁平化的工具类型
type Merge<T extends Record<string, any>, U extends Record<string, any>> = Omit<T, keyof U> & U;
type MergeFlattened<T extends Record<string, any>, U extends Record<string, any>> = {
    [K in keyof (Omit<T, keyof U> & U)]: (Omit<T, keyof U> & U)[K];
};

type RunConfig<
    TContext extends Record<string, any>,
    TChildren extends Record<string, RunConfig<any, any>> = {}
> = {
    context: TContext;
    // fn的context参数为自身上下文+祖先上下文的合并类型
    fn: <Ancestors extends Record<string, any>>(arg: { context: MergeFlattened<Ancestors, TContext> }) => any;
    children: TChildren;
};

function run<
    TContext extends Record<string, any>,
    TChildren extends Record<string, RunConfig<any, any>>,
    TAncestors extends Record<string, any> = {}
>(config: RunConfig<TContext, TChildren>, ancestorContext: TAncestors = {} as TAncestors) {
    const mergedContext = { ...ancestorContext, ...config.context };
    config.fn({ context: mergedContext });
    // 递归调用时传递合并后的上下文
    for (const childConfig of Object.values(config.children)) {
        run(childConfig, mergedContext);
    }
}

// 测试:子级fn能访问自身和所有祖先的上下文
run({
    context: { key0: "value0" },
    fn({ context }) {
        console.log("root", context.key0);
    },
    children: {
        child1: {
            context: { key1: "value1" },
            fn({ context }) {
                // 类型:{ key0: string; key1: string; }
                console.log("child1", context.key0, context.key1);
            },
            children: {
                child11: {
                    context: { key11: "value11" },
                    fn({ context }) {
                        // 类型:{ key0: string; key1: string; key11: string; }
                        console.log("child11", context.key0, context.key1, context.key11);
                    },
                    children: {},
                },
            },
        },
        child2: {
            context: { key2: "value2" },
            fn({ context }) {
                // 类型:{ key0: string; key2: string; }
                console.log("child2", context.key0, context.key2);
            },
            children: {},
        },
    },
});

内容的提问来源于stack exchange,提问作者Jonathan Sudelko

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 17:18:08