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

如何结合映射类型与泛型函数实现TypeScript类型推断?

问题解答:结合映射类型推断与泛型函数调用的Builder实现

你的需求完全可行,只需调整Builder类型中tree方法的泛型约束,让TypeScript能根据传入的树节点键名自动拼接命名空间,从而实现dataPath的自动推断,同时保留Context的全局类型推断。

修改后的实现代码

type BaseType<Context, DataPath extends string> = (ctx: { data: Context; dataPath: DataPath }) => unknown;

type Join<Root extends string, Leaf extends string> = Root extends '' ? Leaf : `${Root}.${Leaf}`;

type RecursiveTree<Context, Namespace extends string = ''> = {
  [K in string]: RecursiveTree<Context, Join<Namespace, K>> | BaseType<Context, Join<Namespace, K>>;
};

type Builder<Context, Ns extends string = ''> = {
  // 泛型约束确保每个键K对应的值使用拼接后的命名空间
  tree<T extends Record<string, RecursiveTree<Context, Join<Ns, keyof T & string>>>>(tree: T): T;

  leaf: {
    create(handler: BaseType<Context, Ns>): BaseType<Context, Ns>;
  };
};

// 初始化根Builder,指定全局Context类型
const builder = {} as Builder<{ foo: string }>;

// 链式构建示例,自动推断dataPath和Context
const defn = builder.tree({
  innerLeaf: builder.leaf.create((ctx) => {
    // ctx.dataPath 类型自动推断为 "innerLeaf"
    // ctx.data.foo 类型为 string
  }),
  innerTree: builder.tree({
    innerLeaf: builder.leaf.create((ctx) => {
      // ctx.dataPath 类型自动推断为 "innerTree.innerLeaf"
    }),
    innerInnerTree: builder.tree({
      innerLeaf: builder.leaf.create((ctx) => {
        // ctx.dataPath 类型自动推断为 "innerTree.innerInnerTree.innerLeaf"
      })
    })
  })
});

关键调整说明

  1. tree方法的泛型约束优化:
    让tree方法接收的对象T满足:每个键K对应的值都是RecursiveTree<Context, Join<Ns, K>>。TypeScript会自动将当前命名空间Ns与键名K拼接,作为子节点的命名空间前缀,从而为后续的leaf.create提供准确的dataPath类型。

  2. 保留Context的全局推断:
    根Builder初始化时指定的Context类型会通过泛型传递到所有嵌套的子Builder中,无需重复标注,确保所有leaf.create中的ctx.data都能正确推断类型。

  3. 链式API的友好性:
    保持了原有的链式调用风格,用户无需手动定义RecursiveTree类型,直接通过builder.tree和builder.leaf.create即可构建嵌套结构,同时获得完整的类型推断支持。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 14:25:44