Tengo un mapeador que toma cualquier objeto, y si hay una clave llamada 'campos' en él, se reemplaza por su contenido. Así por ejemplo,
const obj = { x: 'hey', fields: { y: 'you' } } const mapped = contentfulMapper(obj); // mapped = { x: 'hey', y: 'you' }O
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"] }, }, }; */ Sin embargo, devolver un Record<string, unknown> no es bueno, así que mi pregunta es ¿cómo puedo escribir esto correctamente?
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; };Aquí hay una solución de trabajo. Por favor revísalo, avísame si no obtienes nada. Agregaré cualquier explicación necesaria, entonces.
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 Tenga en cuenta que he agregado comentarios de la directiva del compilador // @ts-ignore para eliminar el error de profundidad infinita.