Derived a key from an enum member to be used as an interface key

I am attempting to configure an object property by selecting its key using a computed key, via an enum, as shown in the code snippet below.

Unfortunately, solutions 1 and 2 are not functioning. These are the solutions I need to implement in my code because I have to assign a value to a key that is dynamically retrieved from an API call.

Interestingly, solutions 4 and 5 are working fine, indicating that solutions 1 and 2 could potentially work by reassigning property values with the dynamic key following a hard-coded property key.

This is because TypeScript accepts an arbitrary number of additional properties as long as you provide matching interface properties. However, I am puzzled as to why duplicated dynamic properties are working in solutions 4 and 5.

So, why are solutions 1 and 2 not working? What am I missing?

Thank you for your assistance.


interface Cat {
    tail: boolean;
    legs: Quadruped;
}

interface Quadruped {
    totalNumber: number;
}

enum BodyParts {
    TAIL = 'tail',
    TOTAL_NUMBER = 'totalNumber'
}

let brokenArgo: Cat = { tail: true, legs: { totalNumber: 4 } };
let argo: Cat = { tail: true, legs: { totalNumber: 4 } };

argo.legs = null;

let TN = 'TOTAL_NUMBER';
let tn: BodyParts = BodyParts[TN];

// solution 1 = broken
brokenArgo.legs = { [tn]: 6 }
// solution 2 = broken
brokenArgo.legs = { [BodyParts[TN]]: 6 };
// solution 3 = working
argo.legs = { [BodyParts['TOTAL_NUMBER']]: 6 };
// solution 4 = working
argo.legs = {
    totalNumber: 0,
    [tn]: 6
};
// solution 5 = working
argo.legs = {
    totalNumber: 0,
    [BodyParts[TN]]: 6
};

You can observe the script above running in this Typescript playground

Answer №1

Upon reviewing some unofficial TypeScript documentation, I came to the realization that when interfaces are declared with specific expected properties (non-optional), it is necessary to inform TypeScript that a computed key is being used "as" the expected property. In other words, I am certain (I expect) to receive that exact property from the backend.

To correct the above (albeit a poor example), it can be fixed as follows:

(refer to live code here)

interface Cat {
    tail: boolean;
    legs: Quadruped;
}

interface Quadruped {
    totalNumber: number;
}

enum BodyParts {
    TAIL = 'tail',
    TOTAL_NUMBER = 'totalNumber'
}

let brokenArgo: Cat = { tail: true, legs: { totalNumber: 4 } };
let argo: Cat = { tail: true, legs: { totalNumber: 4 } };

argo.legs = null;

let TN = 'TOTAL_NUMBER';
let tn: BodyParts.TOTAL_NUMBER = BodyParts[TN];

// solution 1 = broken
brokenArgo.legs = { [tn]: 6 }
// solution 2 = broken
brokenArgo.legs = { [BodyParts[TN as 'TOTAL_NUMBER']]: 6 };
// solution 3 = working
argo.legs = { [BodyParts['TOTAL_NUMBER']]: 6 };
// solution 4 = working
argo.legs = {
    // totalNumber: 0, // No more needed
    [tn]: 6
};
// solution 5 = working
argo.legs = {
    totalNumber: 0,
    [BodyParts[TN]]: 6
};

Therefore, if you are uncertain whether you will receive propertyA or propertyB from the backend (which was my initial issue), and you wish to use a computed key, it is advisable to either make the key in the interface declaration optional or set it as an alias type (or an enum) like

type myType = 'propertyA' | 'propertyB'
and utilize it as the key of the property in your interface as shown below:

interface MyInterface { 
    myType: number 
}

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

Issue with MUI icon import: React, Typescript, and MUI type error - Overload does not match this call

Within my component, I am importing the following: import LogoutIcon from "@mui/icons-material/Logout"; import useLogout from "@/hooks/auth/useLogout"; const { trigger: logoutTrigger } = useLogout(); However, when utilizing this compo ...

Can we access global variables directly in an Angular 2 HTML template?

After setting the app.settings as shown below public static get DateFormat(): string { return 'MM/DD/YYYY';} I need to utilize it in one of my HTML templates for a component. This is what I want to achieve. <input [(ngModel)]="Holiday" [dat ...

What is the most effective method for merging two arrays in JavaScript?

Can a function be created to generate an array like the following? ["0-AA", "0-BB", "1-AA", "1-BB", "2-AA", "2-BB", "3-AA", "3-BB"] This particular function combines two array ...

typescript makeStyles() functions from material-ui library

I've been struggling to find the correct type without relying on any. I have a working code that styles the component as expected: import { makeStyles } from '@material-ui/core/styles' const useStyles = makeStyles((theme) => ({ mainC ...

Discovering ways to fetch an array of objects using object and arrays in JavaScript

When comparing an array of objects with a single object and listing the arrays in JavaScript, specific conditions need to be met to retrieve the array of objects: If the itemvalue and idvalue are the same, check if the arrobj cid has the same codevalue ...

Develop a set of matching key/value pairs using TypeScript

Looking to develop a custom data type where InputKeys will serve as the keys, and the values will be key/value pairs. The keys should correspond to InputFieldKey, with the value being a string. My current progress includes {[key: string]: string}, but I n ...

What is the best way to set a checkbox to null instead of false using Angular?

I am currently developing a filtering system that allows users to select different parameters to filter through a list of items. For instance, the item list could be a collection of dishes with filters represented as checkboxes labeled as vegan, vegetaria ...

Steps for showing a component (popup modal) just one time with React hooks

Is there a way to implement a popup modal that only appears once using hooks and localStorage? The modal should already appear upon page load. const [showModal, setShowModal] = useState<boolean>(true) return( <ModalIsContractor ...

Creating React components with TypeScript: Defining components such as Foo and Foo.Bar

I have a react component defined in TypeScript and I would like to export it as an object so that I can add a new component to it. interface IFooProps { title:string } interface IBarProps { content:string } const Foo:React.FC<IFooProps> = ({ ...

Bringing Typescript classes into React-scripts does not act as a constructor

I am currently working on integrating a Typescript file into my existing app using Yarn and React-scripts. Encountered error: Module not found, unable to resolve './DiamondNodeModel' import {DiamondNodeModel} from './DiamondNodeModel&ap ...

Need help with creating a unit test for the Material UI slider component? An error message saying "Error: Cannot read property 'addEventListener' of null" is displayed when trying to render the component

Encountered a problem while testing the Material-UI Slider with React-Test-Renderer: Uncaught [TypeError: Cannot read property 'addEventListener' of null] Codesandbox Link import React from "react"; import { Slider } from "@materi ...

Having trouble transferring information between two components in Angular version 14

Having recently delved into the world of Angular, I'm grappling with the challenge of passing data from a parent component to a child component. On my product listing page, clicking on a product should route to the product detail page, but I can' ...

Acquire the URL using Angular within a local environment

I am currently working on a basic Angular project where I have a JSON file containing some data. [{ "name": "Little Collins", "area": "Bronx", "city": "New York", "coverImage": "https://images.unsplash.com/photo-1576808597967-93bd9aaa6bae?ixlib=rb-1.2.1&a ...

Dynamic form groups in Angular: Avoiding the issue of form inputs not binding to form groups

Purpose My goal is to develop a dynamic form in Angular that adjusts its fields based on an array received from the backend. For example, if the array contains ["one", "two", "three", "four"], the form should only ...

Guide on adding up the value of a property within an array of objects using AngularJS

I am receiving an array of results from a Node.js API in my Angular app, and here is how it looks: <div *ngFor="let result of results"> <div *ngIf="result.harmattan.length > 0"> ... </div> <br> .. ...

Dynamically divide canvas screens based on fabricjs dropdown selection

I am attempting to implement split screens in fabric js, such as 1, 2, 4, 8, and 16. The screen should split based on the selection from the dropdown menu. Check out my current code where I have successfully uploaded images. If I click on the images, th ...

What causes the difference in inference for unspecified generics between a simple function call and a null-coalescing operator in TypeScript?

What causes the different types of a and b in the given code snippet? function empty<T>() { return [] as T[] } const defEmpty = empty() function test1(abc: number[]|null) { const a = abc ?? defEmpty const b = abc ?? empty() } Upon testing on t ...

Steps for initiating an Angular 4 project

While most developers have moved on to Angular 5, I was tasked with creating a project using Angular 4. After conducting research for several days, I discovered that downgrading the Angular CLI would allow me to accomplish this. By following this approach, ...

Adding an event listener to the DOM through a service

In the current method of adding a DOM event handler, it is common to utilize Renderer2.listen() for cases where it needs to be done outside of a template. This technique seamlessly integrates with Directives and Components. However, if this same process i ...

Oops! Looks like there's an unexpected error with the module 'AppRoutingModule' that was declared in the 'AppModule'. Make sure to add a @Pipe/@Directive/@Component annotation

I am trying to create a ticket, but I encountered an error. I am currently stuck in this situation and receiving the following error message: Uncaught Error: Unexpected module 'AppRoutingModule' declared by the module 'AppModule'. Plea ...