0

The updateObject function below takes in oldObject of type T and newValues of type Partial<T>, and then assigns any of the values present innewValues to oldObject.

My problem is that if newValues contains properties not present in oldObject, my compiler throws an error. How can I express to the compiler that I want newValues to be reduced to only the properties present in oldObject?

Problematic code

In this code, newValues is of type Partial<Point> and point is of type ThreatPoint.

updateObject(point, newValues);

Error Message

The error I receive says:

Argument of type '(point: ThreatPoint) => (ThreatPoint & Partial<ThreatPoint>) | (RoutePoint & Partial<...>)' is not assignable to parameter of type '(item: ThreatPoint) => ThreatPoint'.
  Type '(ThreatPoint & Partial<ThreatPoint>) | (RoutePoint & Partial<...>)' is not assignable to type 'ThreatPoint'.
      Types of property 'category' are incompatible.
        Type 'Category.ROUTE_POINT' is not assignable to type 'Category.THREAT'.

updateObject Function

static updateObject = <T>(oldObject: T, newValues: Partial<T>) => {
   // Encapsulate the idea of passing a new object as the first parameter
   // to Object.assign to ensure we correctly copy data instead of mutating
   return Object.assign({}, oldObject, newValues);
};

Types/Interfaces

export interface BasicPoint {
  category: Category;
  id: string;
  position: Coordinate;
  title: string;
  type: PointType;
}

export enum RoutePointType {
  START = 'START',
  FINISH = 'FINISH',
}

export enum ThreatPointType {
  OBSTACLE = 'OBSTACLE',
  RIVER = 'RIVER',
}

export type PointType = RoutePointType | ThreatPointType;

export enum Category {
  ROUTE_POINT = 'ROUTE_POINT',
  THREAT = 'THREAT',
}

export interface RoutePoint extends BasicPoint {
  category: Category.ROUTE_POINT;
  speed: number;
  type: RoutePointType;
}

export interface ThreatPoint extends BasicPoint {
  category: Category.THREAT;
  type: ThreatPointType;
}

export type Point = RoutePoint | ThreatPoint;

6
  • But your code should work. Please share the error message Commented Sep 15, 2021 at 19:47
  • By the wat, you you want to avoid mutation, Object.assign is not the best choice because it mutates {} Commented Sep 15, 2021 at 19:55
  • #captain-yossarian All of my code has been added to the post now. Commented Sep 15, 2021 at 22:28
  • There is no error tsplay.dev/m0L3Rm Commented Sep 16, 2021 at 7:40
  • @captain-yossarian That code doesn't even execute when you hit Run. It doesn't compile. Commented Sep 16, 2021 at 23:17

1 Answer 1

1

I tried to manage your use case by coding something like that:

 static updateObject = <T extends {}, X extends T>(oldObject: T, newValues: Partial<X> ): T => {
      
      const newObjectValues = { ...newValues }

      const keysToInclude = Object.keys(oldObject);

      const newValuesKeys  = Object.keys(newValues)

      const newValuesFiltered = newValuesKeys
        .filter(key => !keysToInclude.includes(key))
        .forEach(key => delete newObjectValues[key as keyof T]);

      return Object.assign({}, oldObject, newValuesFiltered);

    };

Basically, I said to the typescript compiler that the function can receive an argument of X type which extends the property of T. Then, I created newValuesFiltered in order to obtain an object only with the keys of T.

Sign up to request clarification or add additional context in comments.

1 Comment

This solution doesn't solve my problem. Using the code in my post, and your solution, I get this error: Type 'Category.ROUTE_POINT' is not assignable to type 'Category.THREAT | undefined'.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.