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

TypeScript递归拆解对象fields键时如何正确定义返回值类型

类型定义与函数改造方案

第一步:实现递归展开类型

通过TypeScript条件递归类型,动态推导展开fields后的对象结构:

type FlattenContentful<T> = 
  // 基础类型/数组直接返回原类型
  T extends Record<string, any> ? 
    // 排除原对象的fields键,合并fields递归展开后的类型
    (Omit<T, 'fields'> & (T extends { fields: infer F } ? FlattenContentful<F> : {})) extends infer Merged ? 
      // 递归处理合并后对象的所有属性值
      { [K in keyof Merged]: FlattenContentful<Merged[K]> } 
    : never 
  : T;

第二步:给函数添加泛型绑定

改造原函数,将输入类型和返回类型做关联,同时补充边界判断优化:

type TContentfulModel = {
  fields?: undefined | Record<string, TContentfulModel>;
};

function contentfulMapper<T extends Record<string, any>>(obj: T): FlattenContentful<T> {
  let out: Record<string, unknown> = {};

  Object.keys(obj).forEach((key) => {
    const value = obj[key];
    if (key === 'fields') {
      const mappedField = contentfulMapper(value as Record<string, any>);
      out = { ...out, ...mappedField };
    } else {
      out[key] =
        // 补充null判断,避免null值进入递归逻辑报错
        typeof value === 'object' && value !== null && !Array.isArray(value)
          ? contentfulMapper(value as Record<string, any>)
          : value;
    }
  });

  return out as FlattenContentful<T>;
}

效果验证

使用你给出的示例测试,可获得精准的类型提示:

const givenObj = {
  a: "sdf",
  w: { 
    a2: 343,
    a3: true,
    fields: {
      a4: "ll",
      q: {
        fields: {
          a5: [2, 4, "lk"]
        }, 
      },
    },
  }, 
};

const mapped = contentfulMapper(givenObj);
// TS自动推导mapped类型为:
// {
//   a: string;
//   w: {
//     a2: number;
//     a3: boolean;
//     a4: string;
//     q: {
//       a5: (number | string)[];
//     };
//   };
// }
// 访问mapped.w.a4、mapped.w.q.a5等属性都可获得完整类型提示

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 20:57:01