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

I'm struggling to include a link in my project card component - I've tried using both the Link tag and anchor tag, but so far, I haven't been successful in

I am having trouble getting the link tag to work properly in my UI. I have tried using both the link and anchor tags, but neither seems to be functioning as expected. Can someone please advise on how to fix this issue? I was expecting the link tag to prop ...

Looking for a more efficient approach to writing React styles for color?

Desire I am interested in customizing colors within Material UI components. Moreover, I aim to develop a React Component that allows for dynamic color switching through Props. Challenge The current approach using withStyles results in redundant and lengt ...

Creating TypeScript models from a JSON response in React components

My Angular 2 application retrieves a JSON string, and I am looking to map its values to a model. According to my understanding, a TypeScript model file is used to assist in mapping an HTTP Get response to an object - in this case, a class named 'Custo ...

"Encountering a Prisma type error while attempting to add a new record

I am currently utilizing Prisma with a MySQL database. Whenever I attempt to create a new record (School), an error of type pops up in the console. Additionally, I am implementing a framework called Remix.run, although it does not seem to be causing the is ...

I am facing a problem with the code for my React login page and router

My form page is built in react and typescript, utilizing JWT tokens on the API side. While there are no issues with the container page, I encountered an error on the index.tsx page where the routers are defined: A TypeScript error occurred in C:/Users/yusu ...

Create typings for object properties in TypeScript

I am inexperienced with TypeScript and am looking to set up types for my object keys. I have explored a few methods to accomplish this, but I am encountering an issue where an error is not triggered when assigning a value of a different type. For example: ...

Can you explain the distinction between Observable<ObservedValueOf<Type>> versus Observable<Type> in TypeScript?

While working on an Angular 2+ project in Typescript, I've noticed that my IDE is warning me about the return type of a function being either Observable<ObservedValueOf<Type>> or Observable<Type>. I tried looking up information abou ...

Transforming JSON in Node.js based on JSON key

I am having trouble transforming the JSON result below into a filtered format. const result = [ { id: 'e7a51e2a-384c-41ea-960c-bcd00c797629', type: 'Interstitial (320x480)', country: 'ABC', enabled: true, ...

Leveraging Vue mixin within a @Component

After implementing the vue-property-decorator package, I encountered an issue when trying to use a mixin method inside the beforeRouteEnter hook. Here is what I initially tried: import { Component, Mixins } from 'vue-property-decorator'; import ...

Setting up a Lerna monorepo with TypeScript: A Comprehensive Guide

I have a central repository with the following details in config.json: "main": "dist/cjs/index.js", "module": "dist/esm/index.js", "es2015": "dist/es2015/index.js", "types": "dist/es2015/index.d.ts", "typings": "dist/es2015/index.d.ts", The repository co ...

Using mat-form-field with the outline appearance seems to be causing some issues

When I change the body direction to RTL, the mat-form-field with appearance"outline" seems to have some issues. If you go to the https://material.angular.io site and navigate to the Form field examples, under the Form field appearance variants section, yo ...

The BooleanField component in V4 no longer supports the use of Mui Icons

In my React-Admin v3 project, I had a functional component that looked like this: import ClearIcon from '@mui/icons-material/Clear' import DoneIcon from '@mui/icons-material/Done' import get from 'lodash/get' import { BooleanF ...

Ways to prevent the need for explicit undefined checks when passing a string prop to a component in TypeScript

Can the checkVar function be modified to prevent the occurrence of an error message TS2322: Type string | undefined is not assignable to type string? // The TestComponent function takes a parameter fooProp that should be a string. function TestComponent({ ...

Using ngFor results in duplicate instances of ng-template

I'm facing a challenge with the ngFor directive and I'm struggling to find a solution: <ng-container *ngIf="user.images.length > 0"> <div *ngFor="let image of images"> <img *ngIf="i ...

Guide to setting the order of rendering in React applications

I am currently working with a .tsx file that renders two components: export default observer(function MyModule(props: MyModuleProps) { .... return ( <div> <TopPart></TopPart> <LowerPart>< ...

Include a Custom Button with an Optional Event Handler

I've created a customized material-ui button component: type ButtonProps = { disabled: boolean; text: string }; export function CustomButton({ disabled, text }: ButtonProps) { return ( <Button type="submit" disabled={disabled} ...

Error: The module 'fs' could not be located after running Rollup

Having encountered this issue, I understand that it has been a common question on the internet. I apologize for potentially duplicating the query. Despite trying various solutions found online, none have proven to be effective. The Problem: The problem ar ...

What is the process for including a new item in the p-breadcrumb list?

Having trouble getting my code to add a new item to the p-breadcrumb list on click. Any assistance would be greatly appreciated. Thank you in advance! Check out the live demo here ngOnInit() { this.items = [ {label: 'Computer'}, ...

Utilize the imported function from <Script> within NextJS

When working with vanilla JS, I am able to include a script like this: <head> <script src="https://api.site.com/js/v1/script.js"></script> </head> and then create an instance of it using: const fn = ScriptJS(); I can t ...

An action in redux-toolkit has detected the presence of a non-serializable value

When I download a file, I store it in the payload of the action in the store as a File type. This file will then undergo verification in the saga. const form = new FormData(); if (privateKey && privateKey instanceof Blob) { const blob = new Blo ...