Partial update of an object with typescirpt

Viewed 286

What I am trying to achieve is to create a utility function that excepts an instance and an object and updates the instance with the provided input fields values. Here is the utility function:

function updateEntity<T, K extends keyof T>(
    entity: T,
    updateInput: { [key in K]: T[K] },
  ): void {
    return Object.keys(updateInput).forEach((key) => {
      entity[key as K] = updateInput[key as K];
    });
}

and here I have an instance and an update interface:

class Animal {
  name: string = 'no name';
  isMale: boolean = false;
}

type Input = Partial<{
  name: string;
}>;

const input: Input = {
  name: 'str'
};

const cat = new Animal();

updateEntity(cat, input); // input is an error

I am getting the following error:

Argument of type 'Partial<{ name: string; }>' is not assignable to parameter of type '{ name: string; }'.
  Types of property 'name' are incompatible.
    Type 'string | undefined' is not assignable to type 'string'.
      Type 'undefined' is not assignable to type 'string'.(2345)

I don't think that I fully understand the error message. My intent was to partially update the original instance with the provided input.

The input must be a subset of the original instance (no extra fields).

Here is a link to the playground: link

What am I missing?

1 Answers

Use non-null assertion. When you hover on the updateInput parameter, its type will be { [key in K]?: T[K] | undefined } because the fields that are absent will be undefined. TypeScript still believes that updateInput[key] can be undefined rather than T[K] but we are pretty sure that updateInput[key] can only be of type T[K].

function updateEntity<T, K extends keyof T>(
  entity: T,
  updateInput: { [key in K]?: T[K] },
): void {
  for (const key in updateInput) {
    entity[key] = updateInput[key]!;
  }
}
Related