Utilizing a string variable as a property name for dynamic typing

I am looking to dynamically create a type with a property name based on specified parameters. Although I can successfully create the object, I am facing challenges when trying to create the actual type. This dynamic type creation is essential for composition alterations.

export function mapProp<AssignedType>(value: AssignedType, propertyName: string) {

  type ReturnType = {
    [propertyName]: value
  }; // encountering errors on this line

  return {
    [propertyName]: value
  };
}

The error message received reads:

A computed property name in a type literal must refer to an expression whose type is a literal type or a 'unique symbol' type.

For an example playground, visit: Click here

Answer №1

If you're looking for a solution, something like the following might be your best bet:

export function mapProp<PropertyName extends string, AssignedType>(
  value: AssignedType, 
  propertyName: PropertyName
) {

  type ReturnType = {
    [K in PropertyName]: AssignedType
  }; 
  // similar to Record<PropertyName, AssignedType>

  return {
    [propertyName]: value
  } as ReturnType;

}

In this scenario, utilizing a mapped type instead of an indexed signature is what stands out. By incorporating the generic type parameter PropertyName, it allows for more specific key narrowing beyond just being a string:

const thing = mapProp(123, "abc");
thing.abc; // number
thing.def; // error

With this setup, ReturnType becomes akin to {abc: number}. However, if only a string key is known during compilation, the resulting outcome would resemble:

declare const propName: string;
const stuff = mapProp(123, propName);
stuff.abc; // number
stuff.def; // number 

At that point, ReturnType transforms into {[k: string]: number}, meaning any string key is accepted while assigning it a number value. It may not align with your ideal scenario, but given the circumstances, it's the closest option available.

Additionally, note that without resorting to a type assertion (as ReturnType), computed properties usually default to string indexes rather than anything more precise. This currently reflects a design limitation within TypeScript. While there have been efforts to address this matter (referenced here), no concrete resolution has been implemented into the language yet.

Wishing you all the best with implementing this solution!

Answer №2

There are certainly more refined and straightforward approaches to accomplish this task (visit https://www.typescriptlang.org/docs/handbook/2/mapped-types.html).

Instead of relying on a function for type retrieval, we can now define types in a simpler manner:

export type MappedValues<Key extends string, Value> = {
    [K in Key]: Value
};

let mappedData: MappedValues<'example', number>;
mappedData = {example: 42};  // Works fine
mappedData = {example: "test"}; // Error: Type 'string' is not assignable to type 'number'
mappedData = {anotherExample: true}; // Error: 'anotherExample' does not exist in type ...

// It's also possible to specify multiple properties and treat them as variables
type MultiTypes = [
    {property: 'prop_bool'; data: boolean},
    {property: 'prop_str'; data: 'xyz'}
];

type InitialType = MultiTypes[0];
let result: MappedValues<InitialType['property'], InitialType['data']>;
result = {prop_bool: false}; // Perfectly valid
result = {prop_bool: "wrong"};  // Issue with type declaration

In addition, you can dynamically generate properties based on another string:

type Languages = 'en' | 'fr';
export type DynamicProperties<PropertyKey extends string> = {
    [K in `${PropertyKey}_${Languages}`]: string
};

const trial: DynamicProperties<'testing'> = {
    testing_fr: 'Bonjour',
    testing_en: 'Hello', // If omitted, an error would occur due to missing property...
    testing_es: 'Hola',  // Error: 'testing_es' is not defined ....
};

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

The function cannot be accessed during the unit test

I have just created a new project in VueJS and incorporated TypeScript into it. Below is my component along with some testing methods: <template> <div></div> </template> <script lang="ts"> import { Component, Vue } from ...

Ways to access UserProfile in a different Dialogio

For the implementation of a chatbot, I am utilizing Microsoft's Bot Builder framework. However, upon implementing an alternative path to the dialog flow, I noticed that the user's Profile references are getting lost. Here is the code snippet fr ...

Forwarding parameter data type

I am facing an issue with 2 navigation points leading to the same screen 1. this.router.navigate([this.config.AppTree.App.Module.Details.Path], { state: { data: { id: this.TableId } } }); this.router.navigate([this.config.AppTree.App.Module.Details.Pa ...

Converting an HTML page to PDF with Angular using jsPdf and Html2Canvas

[1st img of pdf generated with position -182, 0, image 208,298 ][1]Converting an HTML page to PDF in Angular 8+ using jspdf and Html2canvas has been a challenge. Only half of the page is successfully converted into PDF due to a scaling issue. Printing th ...

Using Html to differentiate input based on type

Looking for input on the code snippet below: <table class="details-table" *ngIf="animal && animaldata"> <tr *ngFor="let attribute of animaldata.Attributes"> <td class="details-property">{{ attribute.AttributeLabel }}& ...

Validating multiple conditions in Typescript by passing them as function parameters

As a beginner in TS/JS, I am looking to validate multiple conditions passed as arguments to a function. For instance, currently I am verifying the user role name, but in the future, I may need to check other conditions. validateUserDetails(): Promise< ...

What are the reasons behind the compilation failure of the react-sortable-hoc basic example when using typescript?

Take a look at this sample code snippet extracted from the official react-sortable-hoc webpage. import React, {Component} from 'react'; ... // Code snippet goes here ... render(<SortableComponent/& ...

Surprising Discovery: TypeScript - node_modules Found in Unusual Directory

Is there a way to make TypeScript imports function properly even if the node_modules directory is not directly in the tree? How can I prevent TypeScript from throwing errors when importing something like rxjs from external/node_modules. For Example: Dir ...

Tips for effectively hydrating Redux state in Next.js upon page refresh?

I'm experiencing an issue with hydrating user data from local storage upon app reload or page refresh. My project utilizes NextJS for the frontend, and for managing state across the application I rely on redux-toolkit and next-redux-wrapper. Upon us ...

Screen remains blank as the React webpage fails to load

Hello, I am having issues with my React page not showing up. Can someone please review my code to see if there are any errors? Here is the edited program: index.html <!doctype html> <html lang="en"> <head> <meta charset ...

Issues with hydrating React local storage hook in custom implementation within NextJS

Currently facing an issue while implementing the localstorage hook in NextJS. The error message I am encountering is: Error: Hydration failed because the initial UI does not match what was rendered on the server.. Any suggestions on what might be causing ...

Transfer the current Angular project to operate as a separate entity

After successfully migrating my Angular project from version 13 to version 16 step by step, I am now aiming to make it fully standalone before proceeding with the migration to version 17. I am wondering if there is a utility available that can assist me i ...

Tips for ensuring a method is not invoked more than once with identical arguments

I'm grappling with a challenge in JavaScript (or typescript) - ensuring that developers cannot call a method multiple times with the same argument. For instance: const foo = (name: string) => {} foo("ABC") // ok foo ("123") ...

The attribute 'map' is not found on the data type 'Observable<[{}, {}]>'

Struggling to incorporate map, PublishReplay, and other rxjs functions into Angular6, I keep encountering a compilation error stating "Property 'map' does not exist on type 'Observable<[{}, {}]>'" every time. The same issue arises ...

unable to locate the custom npm package within my service

There is a similar inquiry posted here: My custom NPM Package is not found, but unfortunately, the solution provided did not fix my issue. I am encountering an error stating: "Could not find a declaration file for module '@dc_microurb/common' ...

Utilize switchMap to sequence calls

My goal is to execute rest requests sequentially using switchMap(...) from RxJs. Here is the object: export class Transaction { constructor( public id: string, public unique_id: string, public name: string, public status: string, pu ...

Can you explain the distinction between any[] and [] in TypeScript?

Here is an example that successfully works: protected createGroups(sortedItems: Array<TbpeItem>): any[] { let groups: any[] = []; return groups; } However, the second example encounters a TypeScript error: type any[] not assignable to ...

Managing multiple Sequelize DB connections in NestJS: A guide

I recently came across the example in the NestJS documentation regarding setting up a Sequelize DB connection. I'm curious about how to connect to multiple databases using Sequelize and TypeScript with NestJS. Can anyone provide guidance on this? ...

Design system styled component - "The type of X cannot be explicitly defined without a reference..."

How can I resolve this TypeScript issue? I have a styled component exported from a style.ts file and used in the index.tsx file of my React component: style.ts: import { styled, Theme } from '@mui/material/styles'; type CardProps = { theme? ...

How can you resolve the error message "No overload matches this call." while implementing passport.serializeUser()?

Currently, I am working on implementing passport.serializeUser using TypeScript. passport.serializeUser((user: User, done) => { done(null, user.id) }); The issue I am encountering is as follows: No overload matches this call. Overload 1 of 2, &ap ...