How to use generic object utils with objects described via interfaces in Typescript?

Issue

I have this simple code, basically an object which has an interface defined for it and a generic util to cleanup an object, which takes a generic object as an argument. And when I try to pass this object as and argument to util function, the Typescript is not happy.

// utils.ts
// removes undefined props from a generic object
const removeEmpty = (obj: Record<string, unknown>): Record<string, unknown> => {
  Object.keys(obj).forEach(key => obj[key] === undefined ? delete obj[key] : {});

  return obj;
};

// some api
interface IMyAPIParams {
  first: string;
  second: string;
  extra1?: string;
  extra2?: string;
}

const baseReq: IMyAPIParams = {
  first: "one",
  second: "two",
};

// some code, that may result in some of these extra properties being present
// but with value of 'undefined'

const req = removeEmpty(baseReq);

/**
 * 1.
 * 
 * Error: Argument of type 'IMyAPIParams' is not assignable to parameter of type 
 * 'Record<string, unknown>'.
 * Index signature for type 'string' is missing in type 'IMyAPIParams'
 */

/**
 * 2.
 *
 * const req = removeEmpty(baseReq as Record<string, unknown>);
 *
 * Error: Conversion of type 'IMyAPIParams' to type 'Record<string, unknown>'
 * may be a mistake because neither type sufficiently overlaps with
 * the other.
 * If this was intentional, convert the expression to 'unknown' first.
 */

How do I fix this (without @ts-ignore, of course)? This looks like a very typical JS code to me and I can’t make it work in TS.

Solution

You should add a generic type T to the function so you don’t lose your typing information. Afterwards change unknown to any:

const removeEmpty = <T extends Record<string,any>>(obj: T): Partial<T> => {
  Object.keys(obj).forEach(key => obj[key] === undefined ? delete obj[key] : {});

  return obj;
};

Answered By – Tobias S.

Answer Checked By – Gilberto Lyons (AngularFixing Admin)

Leave a Reply

Your email address will not be published.