Home > Software design >  How can I use TypeScript Generics for a function that can return either objects or arrays?
How can I use TypeScript Generics for a function that can return either objects or arrays?

Time:10-28

Similar to Object is of type 'unknown' typescript generics but that question doesn't specify return types in the question.

I have some existing working JS code that uses the classic JSON.parse(JSON.stringify()) technique to clone objects.

When I first turned it into TS, I used Record<string, unknown> to specify it would return an object with a string key and unknown value.

const deepClone = (
  object: Record<string, unknown> 
): Record<string, unknown> => {
  return JSON.parse(JSON.stringify(object));
};

This works fine. But I then realised the function needed to both accept and return arrays to, so I added:

const deepClone = (
  object: Record<string, unknown> | Array<unknown>
): Record<string, unknown> | Array<unknown> => {
  return JSON.parse(JSON.stringify(object));
};

But this fails testing:

  describe(`deepClone`, () => {
    it(`Deep clones object`, () => {
      const names = ["frank"];
      const original = { names };
      const clone = deepClone(original);
      names.push("nancy");
      expect(clone.names).toEqual(["frank"]);
    });
  });
      Property 'names' does not exist on type 'unknown[]'.

I understand the error - arrays can't have a property 'names' even though objects can.

This calls for generics! So following the generics documentation

const deepClone = <Type>(
  object: Record<string, unknown> | Array<unknown>
): Type => {
  return JSON.parse(JSON.stringify(object));
};

However this still fails with:

error TS2571: Object is of type 'unknown'.

How can I use TypeScript Generics for a function that can return either objects or arrays?

CodePudding user response:

Maybe I'm missing something, but I think it's just a matter of defining a generic type T and using T for the function argument as well as the return value:

const deepClone = <T extends Record<string, unknown> | Array<unknown>>
  (object: T): T => {
  
  return JSON.parse(JSON.stringify(object));
};

const o = deepClone({a: 1});
const a = deepClone(['a', 1]); 
  • Related