Can a utility be used to generate a key name based on another key?

Is it possible to dynamically generate a new interface based on an existing one, with key names derived from the originals?

Here's an example of what I'm trying to achieve:

If we have this source object:

interface Car {
  brand: Brand;
  model: string;
  year: number;
}

I want to declare an interface like this:

interface CarSetters {
  setBrand: (brand: Brand) => any;
  setModel: (model: string) => any;
  setYear: (year: number) => any;
}

However, instead of manually declaring it as shown above, I'd like to be able to use something like:

type CarSetters = Setters<Car>

or

type CarState = Car & Setters<Car>

Answer №1

It appears to be a straightforward mapping:

type Brand = boolean;
interface Car {
  brand: Brand;
  model: string;
  year: number;
};
type CarSetters = {
    [T in keyof Car as `set${Capitalize<T>}`]: (newValue: Car[T]) => any;
};

To prevent symbols from causing complications, you can use:

type CarSetters = {
    [T in keyof Car as T extends symbol ? never : `set${Capitalize<T>}`]: (newValue: Car[T]) => any;
};

or

type CarSetters = {
    [T in keyof Car as T extends symbol ? never : `set${Capitalize<T>}`]: (newValue: Car[T]) => any;
} & {
    [T in keyof Car as T extends symbol ? T : never]: Car[T];
};

Answer №2

Thanks to guidance from the helpful CertainPerformance's answer on StackOverflow, I was able to develop a generic solution that perfectly aligns with my requirements. This solution not only answered my initial question but also prompted me to share the generics I crafted:

type Mutators<T> = {
  [K in keyof T as K extends string ? `update${Capitalize<K>}` : never]: (
    updatedValue: T[K]
  ) => any;
};

type ModifiedState<T> = T & Mutators<T>

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 I run a Redis Server on a port other than the default 6379 port using ioredis?

I recently came across this query regarding starting redis-server on a different port than the default 6379 in Ubuntu. I am currently trying to achieve the same using ioredis in my NestJS project, but unfortunately, it refuses to connect to any other port ...

When the imagepath in an Angular application is not updated, it will revert to null

Below is the code snippet that I am currently working on: <div class="col-sm-4 pl-5"> <img attr.src="{{item?.imagePath}}" required height="200" width="200"> </div> In my TypeScript file (ts), I have the following function: editBlog ...

Angular 6 is experiencing an issue with the functionality of the file toggle JS

Currently, I am utilizing the file toggle.js within the Urban theme. In the HTML chatbox, using the img, the file toggle.js is hardcoded and is functioning properly. However, when implementing code in Angular 6, the toggle.js is not functioning as expecte ...

How can one break down enum values in typescript?

I've defined an enum in TypeScript as shown below: export enum XMPPElementName { state = "state", presence = "presence", iq = "iq", unreadCount = "uc", otherUserUnreadCount = "ouc", sequenc ...

Acquire XML documentation for overloaded functions in Typescript

Is it possible for an overloaded function in a subclass to automatically inherit the XML documentation from its base class? When hovering over myFunc I want to be able to see the documentation from the base class when I hover over myFunc, rather than ju ...

Tips for encapsulating a promise while maintaining the original return type

In my code, there is a function that utilizes an axios instance and is of type async function register(data: RegisterData): Promise<AxiosResponse<UserResponse, any>> export const register = (data: RegisterData) => api.post<UserResponse> ...

The MaterialTable component is indicating that there is no property called 'tableData' on the IPerson type

Incorporated an editable attribute to my MaterialTable component. Currently looking for a way to retrieve the index of updated or deleted items within the onRowUpdate and onRowDelete methods. To replicate the issue, refer to this minimal sandbox example: ...

Angular 2 - update browsing history by replacing instead of adding to it

Is it possible to replace the history instead of pushing a new one in Angular 2's new router (rc.1)? For instance, suppose I am in a question list (/questions), then open a new modal in a new route (/questions/add). After adding a new question, I nav ...

Does an async function get automatically awaited if called in a constructor?

I am currently working on updating some code due to a library upgrade that requires it to be made async. The code in question has a base class that is inherited by other classes, and I need to call some functions in the constructor that are now asynchronou ...

Navigating an array using ngFor and encountering an error message saying, "Identifier not specified"

While using ngFor to iterate through an array, I encountered the following error message: "Identifier 'expenseitem' is not defined. The component declaration, template variable declarations, and element references do not contain such a memb ...

What is the best way to create a generic variable and function that utilize the same type?

I am looking for a structure similar to interface propType { value: T action: (value: T) => void } The variable T can be any type, but it must be consistent for both value and action. Using any is not suitable as it could lead to a mismatch in ty ...

Building a dropdown menu component in react native

Looking to implement a dropdown menu in React Native using TypeScript. Any suggestions on how to achieve this for both iOS and Android platforms? Check out this example of a dropdown menu ...

Troubles with Angular2 template parser when parsing HTML fragment that is W3C validated

The online W3C validator successfully parses the following document: <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <title>test</title> </head&g ...

Angular error: The object prototype can only be an Object or null, not undefined

My project was functional until I decided to install ExcelJs (npm install --save exceljs) and things took a turn for the worse. I started encountering errors such as Cannot read property ‘Minus’ of undefined and Cannot find module 'postcss-value- ...

Search through an array of objects and assign a new value

I am facing a challenge with an array of objects structured as shown below: [ { "e_id": "1", "total": 0 }, { "e_id": "3", "total": 0 } ] My objecti ...

Next.js is perplexing me by throwing an error about Event handlers not being able to be passed to Client Component props, even though the component clearly has "use client" at

My bundler generates a basic React component like this "use client"; "use strict";var a=Object.create;var r=Object.defineProperty;var b=Object.getOwnPropertyDescriptor;var i=Object.getOwnPropertyNames;var l=Object.getPrototypeOf,s=Objec ...

Implementation of a function in Typescript that can be defined with a

I am currently diving into the Typescript specification and I'm facing a challenge in creating a functional implementation for describable functions. https://www.typescriptlang.org/docs/handbook/2/functions.html The provided example lacks completene ...

NextImage's ImageProps is overriding src and alt properties

I've created a wrapper called IllustrationWrapper that I'm utilizing in different components. import Image, { ImageProps } from 'next/image'; const getImageUrl = (illustrationName: string) => { return `https://my-link.com/illustra ...

How can CSS variables be applied to a hover property within an Angular directive?

Check out the directive below: import { Directive, ElementRef, HostListener } from '@angular/core'; @Directive({ selector: 'd-btn', host: {} }) export class ButtonDirective { constructor(private el: ElementRef){} @HostLis ...

How can I ensure the end of the list is clearly visible?

I'm experiencing an issue where the last element in my list is getting cut off. When I check the console logs, I can see that it's rendering properly. If I remove the 'position: fixed' style, I can see the element at the bottom of the l ...