Creating a custom function that links a property from one object to a different property

I'm currently working on a function that can map a property from one object to another.

For example, the mapProperty function can be utilized like this:

mapProperty('_id', 'id', { _id: "arst" } )
. This should result in the type { id: string }.

The function mapProperty is defined below. However, I believe that using as unknown as M is not ideal. Without it, I encounter the following error:

Type 'T & { [x: string]: T[K]; }' is not assignable to type 'M'.
'M' could potentially have any unrelated type compared to 'T & { [x: string]: T[K]; }'.

It's also not feasible to define the return value as : { [key: L]: T[K] }.

Is there a better solution to this problem?

const mapProperty = <T, K extends keyof T, M, L extends keyof M>(
  from: K,
  to: L,
  obj: T,
): M => {
  const prop = obj[from];
  delete obj[from];
  return ({ ...obj, [to]: prop } as unknown) as M;
};

Edit: (I used Николай Гольцев's answer with a slight modification)

I have introduced an additional parameter which is a function responsible for mapping the value of the target property:

const mapProperty = <T, K extends keyof T, L extends string, M = T[K]>(
  obj: T,
  from: K,
  to: L,
  fn?: (x: T[K]) => M,
): Omit<T, K> & { [key in L]: M } => {
  const prop = fn ? fn(obj[from]) : obj[from];
  delete obj[from];
  return { ...obj, [to]: prop } as any;
};

Answer №1

To achieve this outcome, the following syntax can be utilized:

const modifyProperty = <T, K extends keyof T, L extends string>(
  source: K,
  target: L,
  object: T,
): Omit<T, K> & {
    [key in L]: T[K]
} => {
  const property = object[source];
  delete object[source];
  return { ...object, [target]: property } as any;
};

const result = modifyProperty('_id', 'id', { _id: "arst", k: true } )

The issue arises in the return statement due to the delete operator not affecting the variable's type. Even after deletion, it remains of type T. Based on my understanding, a computed property always yields the type { [x: string]: T }.

Answer №2

Check out the solution below for a different approach:


type Data = { age: 1, name: 'string' }

// Main utility
type Replace<O, P1 extends keyof O, P2 extends string> =
    Pick<O, Exclude<keyof O, P1>>
    & { [P in P2]: O[P1] }


type Result = Replace<Data, 'name', 'surname'>

const mapProperty = <O, P1 extends keyof O, P2 extends string>(obj: O, from: P1, to: P2): Replace<O, P1, P2> => {
    const { [from]: replaced, ...rest } = obj;

    // It's recommended not to use the delete operator due to its drawbacks and slowness. Instead, consider using Reflect.deleteProperty() as an alternative. Dealing with unknown casting can be tricky when working with computed properties like [to].
    return {
        ...rest,
        [to]: replaced
    } as unknown as Replace<O, P1, P2>
}

const data: Data = { age: 1, name: 'string' }

const result = mapProperty(data, 'age', 'year')
type Res = keyof typeof result // "year" | "name"

Avoiding the delete operator is advisable due to performance reasons and potential issues. Consider using Reflect.deleteProperty() as a better alternative. Handling unknown types and casting can be challenging, especially with dynamic property names like [to].

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

JS module declaration does not identify as a file module

Check out this link for the Angular project If you take a look at src/mock-data/mock-data.js, you'll see that it's quite large but very straightforward. System.register([], function (exports) { mockdata = {......}; exports("MockData", mockd ...

Mocking Multiple Instances of Classes in Jest

I am currently working on a project where I have a class that creates multiple instances of the same object. I am trying to mock this behavior in jest, but I keep encountering an error specifically for the second instance creation test. Error: expect(rece ...

The type 'Observable<boolean>' cannot be assigned to type 'Observable<UserRegistration>'

function completeRegistration(email: string, password: string, firstName: string, lastName: string, location: string): Observable<UserDetails> { let body = JSON.stringify({ email, password, firstName, lastName,location }); let headers = new H ...

Error message: "The function app.functions is not a valid function in Angular Fire Functions

Currently, I am utilizing Angular v16 alongside Angular Fire v16 and Firebase v9. Following the instructions, I completed all the necessary setup steps including executing firebase login, firebase init, and converting the functions to typescript. Next, wi ...

Enhance autocomplete functionality by incorporating a left icon feature for text fields within the autocomplete component

I have a component with autocomplete functionality that displays tags Autocomplete with tags and I am trying to add a left icon, but only the right icon is functioning correctly. Current Issue When I add a left icon, it shows up but prevents the renderi ...

The Angular Tailwind utilities are functioning smoothly during development, but encountering issues in production environments

I've noticed that when I compile my Angular project using Tailwind CSS, I sometimes receive the message "No utility classes were detected in your source files," and other times it builds without any warnings but the utilities are still missing. This i ...

Boosting NestJS Performance with PM2 Multiple Instances

Currently, I have a NestJS application that I typically deploy using PM2 in cluster mode with multiple instances running. One thing to note is that NestJS utilizes the Singleton pattern for its services. This is crucial for some of my features, as it allo ...

Find the distinct elements in two arrays of objects using TypeScript

There are two object arrays available: first = [ { "id": "15", "name": "raza" }, { "id": "1", "name": "sahir" }, { "id": "54", "name": "ayyan" }, { "id": "3", "name": "tahir" }, ]; second = [ { "id": "15", "name": "razi" }, { "id": "3", "na ...

What is the keyboard shortcut in VSCode for creating a new local variable and assigning the result of an expression to it?

When working in Eclipse, there is a useful shortcut (default CTRL + 2 + L) that can create a new local variable to store the result of a selected expression. For example... this.doSomeCalculation(); If you position the cursor over the line above and use ...

I am interested in creating an input range slider using React and TypeScript

This code was used to create a slider functionality import { mainModule } from 'process'; import React, { useState } from 'react'; import styled from 'styled-components'; const DragScaleBar = () => { const [value, setV ...

How to stop a loop of method calls that return a Promise<any> in TypeScript

My current issue involves a loop in which a method is called, and the method returns an object of type Promise<any>. I need to break the loop if the response from the method is correct. However, using the break statement does not stop the loop as exp ...

Linking typescript error messages with their respective compiler configurations (tsconfig.json)

Is there a way to identify which compiler option corresponds to a specific Typescript error? While working with Typescript in VSCode, I often encounter errors like initializer provides no value for this binding element. (Please note that these are warnin ...

Incorporating route links into Material UI tabs in React using TypeScript

Looking for a way to incorporate route links into my tabs in React using TypeScript. How can I achieve this in my code? import React from 'react'; import { makeStyles, Theme } from '@material-ui/core/styles'; import AppBar from '@m ...

Unable to execute a join operation in TypeScript

I have an array of objects listed below var exampleArray = [{ "isAvailable": true, "receipent": [{ "id": "a6aedf0c34", "receipentName": "ABC" }, { "id": "a6aedbc34" ...

Postpone the initial click action triggered by the specified directive

Is it possible to create a directive that prompts for confirmation when a button is clicked? This would involve storing the original event and only executing it once the user confirms their choice. A similar behavior has been mocked here: https://stackbl ...

Trigger an action upon checking a checkbox in an Angular2 application

As a newcomer to Angular2 and web development in general, I am looking to implement an action that will modify a parameter value of an object in the Database when a checkbox is checked or unchecked using Material-Design. I attempted to use [(ngModel)], but ...

Maintain the Http Connection request header active when using Angular with Spring Security

During the process of converting my AngularJS project into Angular 4, I encountered an issue with maintaining the Http connection after logging into the application. The request I am making is: POST /myapp-services/login.html?username=admin&password= ...

Issue in React Native and Firestore: The 'auth' property is not recognized in the specified type or an error object of unknown type

I am currently using Typescript in conjunction with Firebase and React Native. While working on the signup screen, I included Firebase like so: import * as firebase from 'firebase/app'; import 'firebase/auth'; In my onSignUp function, ...

Using a promise as a filter callback in JavaScript: A guide

UPDATE: The solution can be found below I have a multitude of components that need to be filtered based on certain properties, but I am encountering an issue where I cannot resolve the promise before using it in the Array.filter() method. Here is my curr ...

Array of generic types in Typescript

Here's a method that I have: getFiveObjectsFromArray(array: T[]) { return array.slice(0, 5); } I've been using this method multiple times. Is there a way in TypeScript to pass a generic argument instead of using multiple types? Also, when ...