Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

198
Views
How do I iterate through child objects and convert all non-object values to string

I currently have an existing method where I convert all key value pairs to string

export function convertObjValueToString<T>(data: Record<string, T>): Record<string, string> {
  return Object.keys(data).reduce((acc, key) => {
    return {
      ...acc,
      [key]: String(data[key]),
    };
  }, {});
}

but it is currently limited to the first level of an object. I want it to test deeper if for instance the value is another child object

{ 
    id: 2, 
    metadata: { 
        booleanValue: false, 
        someOtherKey: { 
            booleanValue: false
        }
    }
}

expected result would be:

{ 
    id: '2', 
    metadata: { 
        booleanValue: 'false', 
        someOtherKey: { 
            booleanValue: 'false'
        }
    }
}
about 4 years ago · Juan Pablo Isaza
3 answers
Answer question

0

You can use Object.entries() to convert the object to an array of [key, value], map the array of pairs and transform according to type, and then convert back to an object using Object.fromEntries():

const convertObjValueToString = data => {
  if (Array.isArray(data)) return data.map(convertObjValueToString)

  if (data !== null && typeof data === 'object') return Object.fromEntries(
    Object.entries(data)
    .map(([k, v]) => [k, convertObjValueToString(v)])
  )

  return String(data)
}


const obj = {
  id: 2,
  metadata: {
    booleanValue: false,
    someOtherKey: {
      booleanValue: false,
      nullKey: null,
      undefinedKey: undefined,
    }
  }
}

const result = convertObjValueToString(obj)

console.log(result)

Types

You'll need to use a recursive type both to describe the original object, and then resulting object (TS Playground):

type NestedValues<T> =
  | T
  | { [property: string]: NestedValues<T> }
  | NestedValues<T>[];

const convertObjValueToString = (data: NestedValues<any>): NestedValues<string> => {
  if (Array.isArray(data)) return data.map(convertObjValueToString)

  if (typeof data === 'object') return Object.fromEntries(
    Object.entries(data)
    .map(([k, v]) => [k, convertObjValueToString(v)])
  )

  return String(data)
}
about 4 years ago · Juan Pablo Isaza Report

0

export function convertObjValueToString<T extends object>(data: Record<string, T>): Record<string, string> {
  return Object.keys(data).reduce((acc, key) => {
    const currentPropValue = data[key];
    if(currentPropValue === Object(currentPropValue)) {
      return {
        ...acc,
        [key]: convertObjValueToString(currentPropValue)
      };
    }

    return {
      ...acc,
      [key]: String(data[key]),
    };
  }, {});
}

I got the solution right but I having problem fixing the types though as I am getting a argument of type 'T' is not assignable to parameter of type 'Record<string, object>' under my if condition

about 4 years ago · Juan Pablo Isaza Report

0

Consider this example:

type Values = number | boolean | string
type Dictionary = { [prop: string]: Dictionary | Values }

const isObject = (data: unknown): data is Dictionary => 
  typeof data === 'object' && data !== null

type ObjToString<Obj extends Dictionary> = {
  [Prop in keyof Obj]:
  (Obj[Prop] extends Dictionary
    ? ObjToString<Obj[Prop]>
    : (Obj[Prop] extends Values
      ? `${Obj[Prop]}`
      : never)
  )
}

const record = <
  Key extends PropertyKey,
  Value
>(key: Key, value: Value) =>
  ({ [key]: value })

const merge = <
  Obj extends Dictionary,
  Part extends Dictionary
>(obj: Obj, part: Part) =>
  ({ ...obj, ...part })

const convert = <
  Data extends Dictionary
>(data: Data): ObjToString<Data> =>
  Object.keys(data).reduce((acc, elem) => {
    const value = data[elem];

    const maker = isObject(value) ? convert : String

    return merge(acc, record(elem, maker(value)))
  }, {} as ObjToString<Data>)


const result = convert({
  id: 2,
  metadata: {
    booleanValue1: false,
    someOtherKey: {
      booleanValue2: false
    }
  }
})

const id = result.id // `${number}`
const booleanValue2 = result.metadata.someOtherKey.booleanValue2 // "false"

Playground

Values - represents allowed primitive values of the object

Dictionary - represents allowed shape/interface of argument

isObject - custom typeguard. Check whether passed value is an object or not

ObjToString - type representation of business logic. Converts passed object to object where all primitive values are stringified

record - small helper (can be inlined)

merge - small helper (can be inlined)

convert - main function. Recursively goes through each key and converts value to string or makes recursive call

As you might have noticed result is infered and TS is aware which properties are allowed an which are not. Please keep in mind that I have passed literal value of object. If you pass a reference TS still infer allowed properties but values wll be more wider. For instance instead of "false" you will get "true"|"false"

If your argument should match some interface it is better because in that case TS will infer required properties more precisely

about 4 years ago · Juan Pablo Isaza Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!