Home > Back-end >  Using generic function recursively
Using generic function recursively

Time:06-06

I have a generic function which removes properties from object and returns a new one. However if the object has a nested array of objects ts starts to complain. How can I solve this problem?

interface User {
  id: number;
  name: string;
  items?: User[];
}

const user: User = {
  id: 1,
  name: 'test',
  items: [
    {
      id: 1,
      name: 'item',
    },
  ],
};

function transformObject<T>(fields: (keyof T)[], obj: T): T {
  const newObject = {} as T;

  for (const key in obj) {
    if (!fields.includes(key)) {
      newObject[key] = obj[key];
    }

    if (Array.isArray(obj[key])) {
      newObject[key] = [];

      for (let j = 0; j < obj[key].length; j  ) {
        newObject[key].push(transformObject(fields, obj[key][j]));
      }
    }
  }

  return newObject;
}
const result = transformObject(['id'], user);
console.log(result);

Playground

CodePudding user response:

You have two problems here.

TypeScript is not powerful enough to realise that newObject[key] should be of type array when obj[key] is an array. You'll have to manually infer the type since undefined[] does not necessarily exist on type T.

newObject[key] = [] as unknown as T[typeof key];

Furthermore you can't check a type on an item in an array if you need that type information, because TypeScript is not powerful enough to keep track of that.

Instead of checking if obj[key] is of type array, move this into a new variable and then perform the checks

const val = obj[key];

if (Array.isArray(val)) {
    newObject[key] = [] as unknown as T[typeof key];
  
    for (let j = 0; j < val.length; j  ) {
        newObject[key].push(transformObject(fields, obj[key][j]));
    }
}

and lastly newObject[key] is not necessarily an array and you therefore need to explicitly tell TypeScript what type it is

(newObject[key] as undefined as T[]).push(transformObject(fields, obj[key][j]));

There might be a great solution here where types are inferred and everything just works, but sometimes, especially when we're dealing with generic type recursion, you'll just need to cast types.

  • Related