What is the best way to depict object key replacements within a Typescript definition?

I currently have these types:

type PossibleKeys = number | string | symbol;
type ValueOf<T extends object> = T[keyof T]; 
type ReplaceKeys<T extends Record<PossibleKeys, any>, U extends Partial<Record<keyof T, PossibleKeys>> = 
  Omit<T, keyof U> & { [P in ValueOf<U>]: T[keyof U] };

... however, while it is partially working, it's throwing the following error:

Type 'U[keyof U]' cannot be assigned to type 'string | number | symbol'.


A simple demo:

interface Item {
  readonly description: string;
  readonly id: string;
}

interface MyInterface {
  readonly id: string;
  readonly propToReplace: number;
  readonly anotherPropToReplace: readonly Item[];
}

type ReplacedUser = ReplaceKeys<MyInterface, { propToReplace: 'total', anotherPropToReplace: 'items' }>;

In the ReplacedUser type, I can see that it is almost correct. The inferred type is:

{ id: string; total: number | readonly Item[]; items: number | readonly Item[]; }

However, I am expecting:

{ id: string; total: number; items: readonly Item[]; }

What am I doing incorrectly? I would like to understand how to specify that P should receive the values passed in U to prevent Typescript errors and then obtain the correct type for a specific value.

Answer №1

To simplify the process, consider reversing the type parameter U for your function ReplaceKeys:

type PossibleKeys = number | string | symbol;
type ReplaceKeys<T extends {}, U extends Record<PossibleKeys, keyof T>> = Omit<T, ValueOf<U>> & {
    [K in keyof U]: T[U[K]]
};

You can implement this as follows:

type ReplacedUser = ReplaceKeys<MyInterface, { total: 'propToReplace', items: 'anotherPropToReplace' }>;

If altering the shape of U is not an option, handling it becomes more challenging:

// Types mentioned earlier in the post
interface Item {
  readonly description: string;
  readonly id: string;
}

interface MyInterface {
  readonly id: string;
  readonly propToReplace: number;
  readonly anotherPropToReplace: readonly Item[];
}

// All possible key types
type PossibleKeys = number | string | symbol;

// Helper type to extract non-nullable values from type T
type DefinedValues<T> = NonNullable<T[keyof T]>;

// Helper type for replacement object - a record with valid keys and matching keys in input type T
// 
// Partial ensures that not all keys of T need to be passed in replacements
type Replacements<T extends {}> = Partial<Record<keyof T, PossibleKeys>>;

// Type to swap object keys for values
type Invert<T extends Replacements<C>, C extends {} = {}> = {
  [N in DefinedValues<T>]: {
    [K in keyof T]: N extends T[K] ? K : never
  }[keyof T]
}

type ReplacedKeys<T extends {}, R extends Replacements<T>> = Omit<T, keyof R | DefinedValues<R>> & {
  [N in keyof Invert<R>]: {
    [L in keyof R]: N extends R[L] ? (L extends keyof T ? T[L] : never) : never;
  }[keyof R]
}

However, be cautious when using the second approach as it may not warn you about duplicate mappings:

type ReplacedUser = ReplacedKeys<MyInterface, { propToReplace: 'total', anotherPropToReplace: 'total' }>;

For testing purposes, visit the playground here.

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

Ensuring accurate date formatting of API responses in TypeScript

My REST API returns data in the format shown below:- {"id": 1, "name": "New event", "date": "2020-11-14T18:02:00"} In my React frontend app, I have an interface like this:- export interface MyEvent { id ...

Retrieve the array from within the string

Any suggestions on how I can extract the array from this string? The current string is: "['Biller.Customer.Data@Taxonomy', 'Product.Platform and Enterprise Services Data.Data@Taxonomy']" I need to extract it to look like thi ...

Having trouble installing npm package in Angular project

After cloning my project from GitLab, I encountered an issue while trying to install the NPM packages. When I ran npm install, an error popped up: https://i.stack.imgur.com/WNT5s.png Upon checking the log file, I found the following error message: 3060 ...

What is the reason behind TypeScript enclosing a class within an IIFE (Immediately Invoked Function

Behold the mighty TypeScript class: class Saluter { public static what(): string { return "Greater"; } public target: string; constructor(target: string) { this.target = target; } public salute(): string { ...

Checking at compile time whether a TypeScript interface contains one or multiple properties

Is there a way to determine if a typescript interface contains at least one property at compile time without knowing the property names? For example, with the interfaces Cat and Dog defined as follows: export type Cat = {}; export type Dog = { barking: bo ...

Error message in Angular states that it is not possible to bind to 'formGroup' because it is not recognized as a property of 'form' within Angular

When trying to extract data from a form using formcontrol, everything seems to be working fine except for the [formcontrol] = "userForm" in the HTML. If I remove this part, the project runs smoothly. I have tried multiple tutorials but none of them seem ...

Utilizing useLocation for Defining Text Styles

I'm currently integrating TypeScript into my project, but I'm encountering an error related to 'useLocation' in my IDE. Any thoughts on what might be causing this issue? import React from "react"; import { useHistory, useLocat ...

How can you run a function in JavaScript or TypeScript that is stored as a string?

Is there a way to call a function that is stored as a string? For example: var dynamicFun = `function Hello(person) { return 'Hello' + person; }` In this case, the dynamicFun variable can store any function definition dynamically, such as: var ...

Element is missing the necessary "key" property. (React and TypeScript)

Upon running the following code for reactJS and typescript, I encountered the error below: I have also included the import statement import 'bootstrap/dist/css/bootstrap.min.css'; in Index.tsx. Is there a solution to resolve this issue? npm s ...

How can I transfer information from a map to a child component?

I'm attempting to transfer a variable from a parent component to a child component using React and Typescript. In my Table component (parent), I have the following map. It sets the 'data' variable as the value of the last element in the arr ...

Having trouble retrieving an Enum within an Angular template?

I am trying to use an enum to read a property of an array. However, I encountered an error with the following code: <ng-container *ngFor="let elem of list"> <div class="ui-g-12 ui-sm-12 ui-md-12 ui-lg-12 ui-xl-12"> &l ...

How to resolve the issue of not being able to access functions from inside the timer target function in TypeScript's

I've been attempting to invoke a function from within a timed function called by setInterval(). Here's the snippet of my code: export class SmileyDirective { FillGraphValues() { console.log("The FillGraphValues function works as expect ...

Using Typescript to replicate Object.defineProperties

Is there a way to emulate Object.defineProperties from JavaScript in Typescript? I am interested in achieving something similar using the syntax of Typescript: Object.defineProperties(someObject.prototype, { property: {get: function() { return v ...

The successful operation of 'Ionic serve --lab' may involve the need to manually save the app.module

We are currently working on an Ionic project that consists of several pages and a single custom provider named request.ts. The issue we are facing is that whenever we launch it using the command ionic serve --lab, the compilation fails (with the error poin ...

Specialized type for extra restriction on enum matching

In my current project, I am dealing with two enums named SourceEnum and TargetEnum. Each enum has a corresponding function that is called with specific parameters based on the enum value. The expected parameter types are defined by the type mappings Source ...

The limitations of Typescript types influence the program's behavior

As a newcomer to the Typescript environment, I am currently developing a test application to familiarize myself with it. However, I have encountered an issue regarding type restrictions that seems to be not working as expected. In my class, I have defined ...

Solving CORS policy error in a MEAN stack application deployed on Heroku

I've been grappling with a CORS policy error in my MEAN stack app for quite some time now. The specific error message I keep encountering is: "Access to XMLHTTPRequest at <my heroku app url.com/login> from origin has been blocked by CORS ...

Using Typescript to import a module and export a sub function

I am currently using mocha for testing a function, but I have encountered an error while running the test file. The structure of my files is organized as follows: server |-test | |-customer.test.ts |-customer.js Here is the content of the customer.js fi ...

Refine the search outcomes by specifying a group criteria

I have a scenario where I need to filter out service users from the search list if they are already part of a group in another table. There are two tables that come into play - 'group-user' which contains groupId and serviceUserId, and 'gro ...

Secure your React TypeScript applications with GraphQL authentication

When users try to log in on my website, I need to verify their authentication using data from a GraphQL API. I referred to this tutorial for guidance: https://www.apollographql.com/docs/react/networking/authentication/ In my GraphQL playground, I execute ...