1

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.

Alex.Me
  • 616
  • 3
  • 11
  • 1
    THe main problem in in indexing. Please see [my answer](https://stackoverflow.com/questions/37233735/interfaces-vs-types-in-typescript#64971386). `interfaces` are not indexed by the default. If you want to make a quick fix, just define `type IMyAPIParams` instead of `interface IMyAPIParams` and voila :D – captain-yossarian from Ukraine Apr 22 '22 at 09:50

1 Answers1

1

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;
};
Tobias S.
  • 21,159
  • 4
  • 27
  • 45