【问题标题】:Type return value when recursively re-mapping keys in object递归重新映射对象中的键时键入返回值
【发布时间】:2021-09-23 18:45:02
【问题描述】:

我有一个映射器,它接收任何对象,如果其中有一个名为“字段”的键,它会被它的内容替换。例如,

const obj = { x: 'hey', fields: { y: 'you' } }
const mapped = contentfulMapper(obj);

// mapped = { x: 'hey', y: 'you' }

或者

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

/* mapped = {
  a: "sdf",
  w: { 
    a2: 343,
    a3: true,
    a4: "ll",
    q: {
      a5: [2, 4, "lk"]
    }, 
  }, 
}; */

虽然返回 Record<string, unknown> 不是很好,所以我的问题是如何正确输入?

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


const contentfulMapper = (
  obj: Record<string, TContentfulModel | unknown>
) => {
  let out: Record<string, unknown> = {};

  Object.keys(obj).forEach((key) => {
    const field = obj[key] as TContentfulModel;
    if (key === 'fields') {
      const mappedField = contentfulMapper(field);
      out = { ...out, ...mappedField };
      
    } else {
      out[key] =
        typeof obj[key] === 'object' && !Array.isArray(obj[key])
          ? contentfulMapper(field)
          : obj[key];
    }
  });

  return out;
};

【问题讨论】:

  • 这里的想法是它会递归地遍历对象的属性以在任何深度和任何属性下搜索fields,并将其带到顶部,传播它?
  • 是的,差不多 - 但不是最高,只是上一层。

标签: javascript typescript types


【解决方案1】:

这是一个可行的解决方案。请通过它,如果没有得到任何东西,请告诉我。那么我会添加任何需要的解释。

type TContentfulModelExtended = {
  fields: TContentfulModelExtended | TContentfulModel
  [key: string]: any
}

type TContentfulModel = {
  [key: string]: any
}

// if `T` has `fields`, we omit that key, keep rest,
// and take it's intersection with the flatten nested fields
type Flat<T extends TContentfulModelExtended | TContentfulModel> = T extends TContentfulModelExtended
  ? Omit<T, 'fields'> & Flat<T['fields']>
  : T

type MyObjType = {
  a: string
  fields: {
    b: number
    c: boolean
    fields: {
      d: number[],
      fields: {
        e: Promise<number>
      }
    }
  }
}

type Flattened = Flat<MyObjType>

const obj: Flattened = {
  a: 'abc',
  b: 3,
  c: false,
  d: [3],
  e: Promise.resolve(3)
}


const contentfulMapper = <O extends TContentfulModelExtended | TContentfulModel>(obj: O) => {
  let out: Flat<O> = {} as any;

  Object.keys(obj).forEach((key) => {
    const field = obj[key];
    if (key === 'fields') {
    // @ts-ignore
      const mappedField = contentfulMapper(field);
      out = { ...out, ...mappedField };
      
    } else {
      out[key] =
        typeof obj[key] === 'object' && !Array.isArray(obj[key])
          ? contentfulMapper(field)
          : obj[key];
    }
  });

  // @ts-ignore
  return out;
};

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

const objSpreaded = contentfulMapper(givenObj)
objSpreaded.a // all props from `a` to `e` works properly
objSpreaded.a2
objSpreaded.a3
objSpreaded.a4
objSpreaded.a5
objSpreaded.f // expected error

[Playground]

请注意,我添加了编译器指令 cmets // @ts-ignore 以消除无限深度错误。

【讨论】:

  • 感谢您的帮助!我想这不是我想要的。如果我理解,那么它会完全变平,而我的地图会在任何深度用它的值替换任何“字段”键。所以例如const givenObj = { a: "sdf", w: { a2: 343, a3: true, fields: { a4: "ll", fields: { a5: [2, 4, "lk"], }, }, }, };应该变成const mappedObj = { a: "sdf", w: { a2: 343, a3: true, a4: "ll", a5: [2, 4, "lk"], }, },};
  • 您能否在问题中以正确的格式添加该示例?
  • 是的 - 我现在已经添加了。
猜你喜欢
  • 2021-09-10
  • 2023-03-23
  • 2017-03-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-11-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多