TypeScript: safely reassigning object properties for type safety

What I aim to achieve

I am looking to create a JavaScript function that can remap property names of the first argument using the second argument.

The goal is to utilize this remap function to generate query string parameters. For instance, transforming

{ param1: 1, param2: 2, param3: 3}
into ?p1=1&p2=2&p3=3.

/**
 * @example
 *
 * const original = { a: 1, b: 'WOW', c: new Date(2019, 1, 1, 0, 0, 0) };  
 * const mapping = { a: 'hello', b: 'world', c: '!!!' };
 * 
 * > remap(original, mapping);
 * { hello: 1, world: 'WOW', '!!!': new Date(2019, 1, 1, 0, 0, 0) }
 */
const remap = (original, mapping) => {
  const remapped = {};
  Object.keys(original).forEach(k => {
    remapped[mapping[k]] = original[k];
  });
  return remapped;
};

My flawed attempt

I made an attempt with the code below, but it has certain flaws.

export const remap = <
  T extends { [key: string]: any },
  U extends { [P in keyof T]: string }
>(original: T, mapping: U) => {
  const remapped: any = {};

  Object.keys(original).forEach(k => {
    remapped[mapping[k]] = original[k];
  });

  // Issues
  // 1. remapped is declared as any, and casting is required.
  // 2. All values are declared as any.
  return remapped as { [P in keyof U]: any };
};

const remapped = remap(
  { a: 1, b: 'text', c: new Date() },
  { a: 'Hello', b: 'World', c: '!!!' }
);

console.info(remapped);

Answer №1

If you want to convert an object into tuples containing property names and types, you can do it using some conditional type magic:

// Function that converts object to tuples of [property name, property type]
type TuplesFromObject<T> = {
    [P in keyof T]: [P, T[P]]
}[keyof T];

// Get property key by value
type GetKeyByValue<T, V> = TuplesFromObject<T> extends infer TT ?
    TT extends [infer P, V] ? P : never : never;

export const remap = <
    T extends { [key: string]: any },
    V extends string,
    U extends { [P in keyof T]: V }
>(original: T, mapping: U) => {
    const remapped: any = {};

    Object.keys(original).forEach(k => {
        remapped[mapping[k]] = original[k];
    });
    return remapped as {
        [P in U[keyof U]]: T[GetKeyByValue<U, P>]
    };
};

const remapped = remap(
    { a: 1, b: 'text', c: new Date() },
    { a: 'Hello', b: 'World', c: '!!!' }
);

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

Using Typescript: accessing all properties from a specified type while excluding one

Currently working in React, I am interested in extending my type from another, with the exception of some props. This is how I want to approach it : import React from 'react'; import { withTheme } from 'styled-components'; import SvgBa ...

What could be causing the issue with my output not displaying correctly?

Hey guys! I'm working on creating a to-do list but I've encountered a problem. Whenever I enter a value in the text field, it doesn't get added to the array of list elements. Strangely enough, when I console.log it, it seems to work. Can any ...

Probability of an event occurring when represented as whole numbers in percentage form

Currently, I'm developing a unique job system within a Discord bot that allows users to mine various types of ores. The probability of receiving specific ores is based on the user's mining skill level, which is stored in a database and can vary a ...

What are the steps to integrate material-ui with styled-components effectively?

import styled from "styled-components"; import Button from "@material-ui/core/Button"; export const StyledButton = styled(Button)` margin: 20px; `; I'm having trouble adjusting the button styling. How can I add a margin to the ...

Nativescript encountered an issue while attempting to generate the application. The module failed to load: app/main.js

I'm currently experimenting with the sample-Groceries application, and after installing NativeScript and angular 2 on two different machines, I encountered the same error message when trying to execute: tns run android --emulator While IOS operations ...

What is the process for discovering the kinds of models that can be generated with a Prisma client?

Ensuring data type correctness when creating a Prisma model named 'bid' is crucial. With auto-generated prisma types available, understanding the naming convention and selecting the appropriate type can be confusing. The bid schema looks like th ...

Retrieve the final variable in an Observable sequence

In my code, I have a variable called 'messages' which stores messages from a conversation: messages: Observable<Message[]>; To populate the 'messages' variable, I do the following: const newMessage = new Message(objMessage); ne ...

Type of tuple without a specific order

Exploring Typescript typings has led me to ponder how to create a type that is a tuple with unordered element types. For example: type SimpleTuple = [number, string]; const tup1: SimpleTuple = [7, `7`]; // Valid const tup2: SimpleTuple = [`7`, 7]; // &ap ...

Error in Svelte/Typescript: Encounter of an "unexpected token" while declaring a type

Having a Svelte application with TypeScript enabled, I encountered an issue while trying to run it: [!] Error: Unexpected token (Note that you need plugins to import files that are not JavaScript) src\api.ts (4:7) 2: 3: export default class API { 4: ...

Utilizing feature flags for Angular modules to enable lazy loading

Can we dynamically change the lazy loaded module based on a specific flag? For instance, loading module A if the flag is active and module B otherwise. The crucial aspect is that both modules should use the same path. Approach #1 - dynamic loadChildren() ...

Creating a type declaration for an object by merging an Array of objects using Typescript

I am facing a challenge with merging objects in an array. Here is an example of what I am working with: const objectArray = { defaults: { size: { foo: 12, bar: { default: 12, active: 12 } }, color: {} } } ...

The application's functionality is interrupted when router.navigate() is called within the .subscribe method

I am having an issue with user navigation on my application. After successfully signing in, users get redirected to the home page (/), but they are unable to navigate by clicking any links on that page. Upon further investigation, I discovered that moving ...

Executing TypeORM commands yields no output

It's been a while since I last tested my Nest project with TypeORM, and now when I try to run any TypeORM command, nothing happens. I attempted to run TypeORM using these two commands: ts-node -r tsconfig-paths/register ./node_modules/typeorm/cli.js ...

Tips for leveraging angular CLI's async import feature with webpack

I've been attempting to utilize webpack's (2.2.1) async module loading as outlined in the documentation. In addition, I have explored various examples for reference. However, I keep encountering the error message Declaration or statement expecte ...

The JSX component cannot be named 'Stack.Navigator' or used as such

Encountering a type issue with react navigation using Stack.Navigation or Stack.Group from createNativeStackNavigator The error message indicates that the types do not match with JSX.element. Specifically, it states: Type '{}' is not assignable ...

The mat-table's data source is failing to refresh and display the latest

When I click on a column header to sort the table, a function should trigger and update the data. However, the data is not updating as expected. Below is the code for the function and the table: <table mat-table #table [dataSource]="dataSourceMD&qu ...

Guide to Implementing StoreApi in Zustand LibraryLearn how to utilize Store

While reading the documentation for zustand, I came across a useful piece of information. In addition to the standard `set` and `get` parameters, there is an extra parameter called `api` in the StateCreator function. Check out the example below: import cr ...

Utilizing dynamic translation ID with Angular's $localize feature

Angular 9 introduces a new feature with @angular/localize that allows code translation directly from typescript. While the official documentation is lacking, I discovered some helpful tips in this post. $localize`:@@my-trans-unit-id:` // This method works ...

ERROR: Unable to call function getTime in Typescript

While working on my function in Typescript, I encountered an issue with two sets of data in the database - date and time, both expecting strings. When users select a date, I trigger a POST request to set the time for them. To handle this scenario, I creat ...

Issue with rendering Base64 image array strings in FlatList component in React Native

In my RN App, I am trying to display a FlatList with Image Items but it seems like I have missed something. I am retrieving blob data from my API, converting it to a String using Buffer, and then adding it to an Array. This Array is used to populate the F ...