Definition of a type for comparing and updating specific object fields in Typescript

Suppose I have two objects and I want to compare and assign specific fields between them:

const left = { a: 1, ignored: 5, something: 7 };
const right = { a: 2, ignored: 6, else: 8, id: 18 };

If I call leftToRight(left, right, ['a']), then the updated right object should be:

{ a: 1, ignored: 6, id: 18 }

After this, I also need to call another function using the id from right.

This is my current implementation:

leftToRight(left, right, keys) {
  let changed = false;
  
  for (const key of keys) {
    if (!Object.is(left[key], right[key])) {
      right[key] = left[key];
      changed = true;
    }
  }

  doSomething(right.id)

  return changed
}

I am facing difficulties in defining the appropriate type for this function :-(

Initially, I tried with:

leftToRight<T>(left: T, right: T, keys: Array<keyof T>): boolean

which resulted in an error "Property 'id' does not exist on type 'T'" as I couldn't find a way to check it ('id' in right)

In my second attempt:

leftToRight<T>(left: T, right: T & {id: number}, keys: Array<keyof T>): boolean

I got the error "Type 'T' is not assignable to type '{ id: number; }' for right[key] = left[key]

For the third try:

leftToRight<T, U extends {id: number}>(left: T, right: U, keys: Array<keyof T & keyof U>): boolean

but again faced an error for the assignment right[key] = left[key] due to the unrelated types T and U.

Answer №1

Revised Response:

Your requirements have been clarified to state that both Left and Right may have unique properties not found in the other. To accommodate this, two generic types are necessary.

In the case of Right, it is an object containing an id. The objective is to ensure that the selected keys exist in both Left and Right, with matching value types. A generic called Keys has been defined as a subset of the keys from Right. Additionally, Left is specified as the subset of Right that includes all these keys.

function leftToRight<Right extends {id: number}, Keys extends keyof Right>(
  left: Pick<Right, Keys>, right: Right, keys: Keys[]
) {
  let changed = false;
  
  for (const key of keys) {
    if (!Object.is(left[key], right[key])) {
      right[key] = left[key];
      changed = true;
    }
  }

  doSomething(right.id)

  return changed
}

Interactive Example Link

Original Explanation:

I experimented with various approaches before arriving at one that functions properly. Admittedly, the definition provided isn't the most refined. During attempts to use generics to define Left and add an id for Right, errors occurred. However, omitting id from Right proved effective.

It is established that Right is an object featuring a property of {id: number}, while Left should possess all properties of Right</code except for <code>id. To ensure elements within keys are shared among both objects, exclusion of the id key from keyof Right is required.

function leftToRight<Right extends {id: number}>(
  left: Omit<Right, 'id'>, right: Right, keys: (Exclude<keyof Right, 'id'>)[]
) {
  let changed = false;
  
  for (const key of keys) {
    if (!Object.is(left[key], right[key])) {
      right[key] = left[key];
      changed = true;
    }
  }

  doSomething(right.id)

  return changed
}

Playground Reference

Answer №2

Experiment using left: Partial<T>, and ensure that left[key] is not undefined by using the exclamation mark !.

Check out this example:

const left = { a: 1, ignored: 5};
const right = { a: 2, ignored: 6, id: 18 };

function leftToRight<T>(left: Partial<T>, right: T, keys: Array<keyof T>) {
  let changed = false;
  
  for (const key of keys) {
    if (!Object.is(left[key], right[key])) {
      right[key] = left[key]!;
      changed = true;
    }
  }

  // doSomething(right.id)

  return changed
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

How can one define a getter within an interface?

One of my classes is structured like this (only showing a portion here): export class LinkedListNode<t> extends windward.WrObject implements ILinkedListNode<t> { public get next(): LinkedListNode<t> { return this._next === thi ...

Having trouble importing a standalone directive into a standalone component

I am currently working on a directive that can capture the coordinates of the parent element and change the position of the child element based on the parent's x and y positions. Both components are standalone, and the directive has already been impo ...

Forming a distinct array from a collection of objects

Describing demoList: demoList = [ { id: 1, validFrom: "2019-06-01T00:00:00", validTo: "2020-06-17T00:00:00", xxxM: 50, xxxN: 2.2, xxxQ45: 2, xxxQ100: 1.65, xxxQ125: null, xxxQ150: null, xxxQ2 ...

What is the best way to determine which function to invoke in ngIf?

Depending on the value of a variable, I need to call either the login() or logout() methods from this.loggedInService.isLoggedIn. If the value of the variable is !this.loggedInService.isLoggedIn, then call login(). If !this.loggedInService.isLoggedIn is ...

Engage with a feature that is currently displayed in a separate window

I'm working on a project that requires me to display 2 different components in separate browser windows. I've tried opening two windows and displaying the components, but now I'm wondering if there's a way for a component in one window ...

Tips for chaining API calls in Angular using rxjs?

How can I efficiently nest API calls in Angular using RxJS? getProducts(): Observable<any> { return this.getProductIDs().pipe( map((response) => response.products.map((data) => (item: any) => flatMap(() => th ...

Exploring the power of dynamic templates in Angular 4

After exploring numerous blog posts and discussions, I have yet to discover a solution for my template dilemma. The issue revolves around our straightforward icon component, where users can specify the desired icon and size directly in the template: impor ...

The ValidationSchema Type in ObjectSchema Seems to Be Failing

yup 0.30.0 @types/yup 0.29.14 Struggling to create a reusable type definition for a Yup validationSchema with ObjectSchema resulting in an error. Attempting to follow an example from the Yup documentation provided at: https://github.com/jquense/yup#ensur ...

All constructors at the base level must share a common return type

I am looking to convert my JSX code to TSX. I have a snippet that refactors a method from the react-bootstrap library: import {Panel} from 'react-bootstrap'; class CustomPanel extends Panel { constructor(props, context) { super(props ...

TS2307 Error: The specified module (external, private module) could not be located

I have come across similar queries, such as tsc throws `TS2307: Cannot find module` for a local file . In my case, I am dealing with a private external module hosted on a local git server and successfully including it in my application. PhpStorm is able ...

What is the method for utilizing string interpolation in Angular/Typescript in order to retrieve a value from a variable?

I have a variable called demoVars, which is an array of objects with properties var1, var2, and var3. In my component class, I have a variable named selectedVar that holds the name of one of these properties: var1, var2, or var3. I want to dynamically pu ...

Unable to establish a value for the input field

I need to verify if an object has a value without generating an error. If it does, I want to set it as the value of an input field. export class DialogServiceTabletAddRowComponent implements OnInit { service?: string; constructor(private _dialogRef: M ...

Encountering an issue when attempting to import a non-source module from a node library while running a Typescript script

I have a script that takes input and utilizes the three.js library to apply geometric transformations to the data. I execute this script using ts-node pipeline.ts. Here is the structure of my project: ├── package-lock.json ├── package.json ├ ...

I am encountering challenges with submitting the form

I am encountering an issue where I want to submit the form on button click and navigate to the next page, but instead I get an error message saying: "Form submission canceled because the form is not connected". Does anyone have a solution for this problem ...

Unable to determine model dependency in Nest

I encountered an issue where Nest is unable to resolve dependencies. The error message from the logger reads as follows: [Nest] 39472 - 17.08.2023, 05:45:34 ERROR [ExceptionHandler] Nest can't resolve dependencies of the UserTransactionRepository ( ...

Parcel is not compatible with basic HTML and TypeScript files

Just recently, I successfully installed parcel by executing the command: npm install -g parcel-bundler Everything went smoothly. Following that, I created a basic HTML file: <html> <body> <script src="./src/index.ts">< ...

Issue with logging messages using console.log in Knex migration script

My concern: I am facing an issue where the console.log('tableNobject: ', tableNobject) does not get logged in my knex migration script. I have attempted the following code snippets: //solution A export async function up(knex: Knex) { const ta ...

managing commitments in TypeScript

Is there a way to convert a promise into a string, or is there another method for handling this result? I am encountering an error stating "You cannot use an argument of type 'Promise' for a parameter of type 'string'." const pokemonIma ...

The inclusion of unions as parameters alters the way errors are handled

This question may have been posed previously, but unfortunately I lack suitable search terms. When parameters are changed to a union, it seems to enforce strict parameter counting. This can lead to: a) the unnecessary requirement of dummy parameters: // ...

Enhancing .gitignore with conditional logic for more efficient file exclusion

In my TypeScript project, I am facing an issue with unnecessary merge conflicts caused by the generated .d.ts and .js files. Since my project is quite large and only halfway converted from JS to TS, I cannot simply ignore all .js files using .gitignore. A ...