Create a class with additional attributes to support different types of options

I define a set of options represented by strings:

export type Category = 'people' | 'projects' | 'topics' | 'tools'

An index is declared as follows:

interface Entry {
  ...
}
type IPostEntryIndex = {
  [name in Category]: Entry
}

How can I create class properties that correspond to the category names?

const categories: Category[] = ['people', 'projects', 'topics', 'tools']

class PostEntryIndex implements IPostEntryIndex {
  // What needs to be included here so that `PostEntryIndex` has a property 
  // for each category listed in Category?

  constructor () {
    categories.map(cat => this[cat] = new Entry())
  }
}

Note: While I could explicitly declare the categories, I am seeking a more efficient solution that doesn't require manual updates when a new category is added. Keeping categories array aligned with Category would also be ideal. Perhaps utilizing an enum could help achieve this.

Answer №1

There isn't a specific syntax that allows you to declare multiple properties in a class all at once. You can create an index signature, but not a union of string literals. Of course, you could manually add each property, but I assume you're looking for a more efficient solution.

One workaround is to use declaration merging to incorporate the properties into the class instance's interface. By defining a class like class PostEntryIndex {}, you generate a value named PostEntryIndex that represents a class constructor, as well as an interface also named PostEntryIndex which mirrors the class structure. With interface merging, you can easily append any desired properties:

interface PostEntryIndex extends IPostEntryIndex { }
class PostEntryIndex {
  constructor() {
    categories.map(cat => this[cat] = new Entry())
  }
}

It's important to note that utilizing this method bypasses strict class property initialization checks enforced by --strict mode. This means you won't receive warnings from the compiler regarding uninitialized properties, although in this context using categories does initialize the necessary properties.

In addition, after merging the interfaces in this manner, the implements clause becomes somewhat redundant, so it has been omitted here. (In truth, implements clauses are often unnecessary due to TypeScript's structural type system allowing objects with compatible structures to be used interchangeably.)

To test if everything functions correctly:

const pei = new PostEntryIndex();
pei.projects; // Entry

Seems to be working fine. Hopefully this information proves useful to you; best of luck!

Playground link for code demonstration

Answer №2

This code snippet is compatible with https://www.typescriptlang.org/play/

export type CategoryList = 'people' | 'projects' | 'topics' | 'tools'

class Entry {
}

type IPostEntryIndex = {
  [item in CategoryList]: Entry
}

const categoriesList: CategoryList[] = ['people', 'projects', 'topics', 'tools']

class PostEntryIndex implements IPostEntryIndex
{
  people: any;
  projects: any;
  topics: any;
  tools: any;

  constructor () {
    categoriesList.map(category => this[category] = new Entry())
  }
}

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

What situations call for the use of 'import * as' in TypeScript?

Attempting to construct a cognitive framework for understanding the functionality of import * as Blah. Take, for instance: import * as StackTrace from 'stacktrace-js'; How does this operation function and in what scenarios should we utilize imp ...

Binding iframes in Angular 6

Is there a way to display iframe code stored in a variable within a div? Here's the HTML code: <div class="top-image" [innerHTML]="yt"></div> And here's the TypeScript code: yt = '<iframe class="w-100" src="https://www.you ...

Ways to address the issue arising from the utilization of the "as" keyword

Every time I encounter this issue - why must I always provide all the details? type Document = Record<string, any> type FilteredDocument<T extends Document> = {[key in keyof T as T[key] extends (()=>void) ? never : key]: T[key]} const ...

Retrieve a list of all file names within a designated directory using Angular

I am working on my Angular app and I need to list all the file names inside the assets folder. To achieve this, I am planning to utilize the npm library called list-files-in-dir https://www.npmjs.com/package/list-files-in-dir Here is the service impleme ...

There is no initial value set for the property and it is not definitively assigned in the constructor

I encountered an issue while working on the following code snippet: export class UserComponent implements OnInit { user: User; constructor() { } ngOnInit() { this.user = { firstName : "test", lastName ...

Automatic verification of OTP in Ionic 3

Seeking assistance for implementing auto OTP verification in a project I am working on. After the user enters their phone number, I have come across some examples for Ionic 1 with Angular 1 online. However, I am specifically looking for examples using Io ...

The Angular tutorial for the "Tour of Heroes" is experiencing issues with aligning the heroes' list properly

I am currently working on the Angular tour of heroes tutorial. However, I am facing an issue when trying to display the list of heroes as it appears like this: It is strange because even though the CSS/HTML/TS code from the tutorial seems correct, the lis ...

What is the best way to send my Array containing Objects to the reducer using dispatch in redux?

I'm currently facing an issue where I can only pass one array item at a time through my dispatch, but I need to pass the entire array of objects. Despite having everything set up with a single array item and being able to map and display the data in t ...

Passing Selected Table Row Model Data to Backend in Angular 7

My goal is to send the selected data in a table row, which I select through a checkbox, to the server. However, I'm unsure about how to handle this via a service call. While I have the basic structure in place, I need assistance with sending the items ...

Error Alert: Redundant Identifier in Angular 2 TypeScript Documents

After following the Angular2 TS Quickstart guide, I noticed duplicate files scattered across various folders in my project. For browser: typings/browser node_modules/angular2/typings/browser Regarding es6-shim: node_modules/angular2/typings/es6-shi ...

I prefer the value to switch to false whenever I navigate to a new route and then return to the previous route, as the sidebar remains open

click here for image details view image description here Struggling to set the value as false when revisiting this site. Need assistance! Could someone lend a hand, please? ...

What is the proper way to declare an array of arrays with interdependent types?

Imagine I am creating a directory of tenants in a shopping center, which can be either shops or restaurants. These tenants fall into various categories: type ShopTypes = | `Accessories` | `Books` | `Clothing`; type RestaurantTypes = | `Div ...

Module '@tanstack/react-table' cannot be located even though it has been successfully installed

Currently, I am tackling a TypeScript React project and encountering an issue while attempting to import ColumnDef from @tanstack/react-table in my columns.tsx file. import { ColumnDef } from "@tanstack/react-table"; export type Payment = { id ...

What is the best way to send serverside parameters from ASP.Core to React?

After setting up a React/Typescript project using dotnet new "ASP.NET Core with React.js", I encountered the following setup in my index.cshtml: <div id="react-app"></div> @section scripts { <script src="~/dist/main.js" asp-append-versi ...

Using Promise.all like Promise.allSettled

I am looking to streamline the handling of Promise.allSettled in a more generic way. Currently when using allSettled, it returns a list of both fulfilled and rejected results. I find this cumbersome and do not want to handle it everywhere in my code. My g ...

Tips on changing the date format in Typescript to the desired format

My date string reads as 2016-09-19T18:10:31+0100. Here's what I'm doing: let dateString:string = 2016-09-19T18:10:31+0100; let newDateString:Date = new Date(dateString); The output I'm currently getting is Tue Sep 19 2016 18:10:31 GMT+0530 ...

Is there a way for me to access the user's gender and birthday following their login using their Google account details?

I have successfully implemented a Google sign-in button in my Angular application following the example provided in Display the Sign In With Google button: <div id="g_id_onload" class="mt-3" data-client_id="XXXXXXXXXXXX-XX ...

The creation of the Angular project was unsuccessful due to the outdated circular-json dependency

After attempting to create a new Angular project with the command: ng new hello-world I encountered an error message: npm WARN deprecated <a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="b5d6dcc7d6c0d9d4c798dfc6dadbf5859b809b8 ...

How can I use TypeScript to wrap a component in Vue 3?

Looking to customize a PrimeVue component (Calendar) by styling it differently and then re-exporting it. Here's an example in React: const WrappedCalendar: React.FC<CalendarProps> = (props)=> <div style={{background:'green'}}&g ...

Collaborating on code between a Typescript React application and a Typescript Express application

Struggling to find a smart way to share code between two interconnected projects? Look no further! I've got a React web app encompassing client code and an Express app serving as both the API and the React web app host. Since I use Typescript in both ...