Limit class generic to specify constructor argument type

I have a unique object that I need to transform into various structures based on its keys. Each key-value pair must be treated individually, so I intend to convert the object into an array of entries, then map those entries into policy objects and finally execute them all. My objective is to utilize this object in the following manner:

type ValuesMap = {key1: Value1; key2: Value2; ...};
const valuesMap: ValuesMap = getValues();
const results = Object.entries(valuesMap).map(([key, value]) => policyFactory.createPolicy(key, value).apply());

To make this possible, I plan to establish an abstract policy class and a factory for these policies:

type Key = keyof ValuesMap;
type Value<K extends Key> = ValuesMap[K];
abstract class Policy<K extends Key> {
  constructor(protected readonly value: Value<K>) {}
  abstract apply();
}

class ConcreteKey1Policy extends Policy<'key1'> {
  //This class's constructor should only accept a Value1 argument; TypeScript should raise an error if any other type is specified
  apply() {/*...*/}
}

//...
class PolicyFactory {
  private readonly constructorMap: {
    [K in Key]: new (value: Value<K>) => Policy<K>;
  } = {
    key1: ConcreteKey1Policy;
    key2: ConcreteKey2Policy;
    //Repeat for all relevant keys in ValuesMap
  };

  createPolicy<K extends Key>(type: K, value: Value<K>): Policy<K> {
    return new this.constructorMap[type](value); //error1
    switch(type) {
      case 'key1':
        return new ConcreteKey1Policy(value); //error2
      //...
    }
  }
}

I have tried two approaches for the factory class, one using a map of possible keys and corresponding constructors, and the other employing a switch for a type parameter.

Both strategies result in TypeScript errors:

  • When attempting to create an object from the constructor map, the error "type '"key1"' is not assignable to type 'K'. '"key1"' is compatible with the constraint of type 'K', but 'K' could potentially be instantiated with a different subtype of the 'Key' constraint."
  • When utilizing the switch statement, every creation of a concrete policy triggers the error "Argument of type 'Value1 | Value2 | Value3 | Value4' cannot be assigned to parameter of type 'Key1'. Type 'Value2' is not compatible with type 'Value1'."

I comprehend the nature of both errors, however, resolving them poses a challenge. I aim to incorporate generics in the factory where the type of value aligns with the type, yet by invoking extends Key, a constraint encompasses any subtype of Key. What I truly require is any type within the union of Key without exceptions. How can such a constraint be established? Is there a simpler method to achieve the desired outcome?

Answer №1

Upon further investigation, it appears that the issue has been resolved with the newer typescript version. The updated code now functions properly as demonstrated in the playground (special thanks to @colinD). This fix is confirmed from version 4.6.4 onwards. I have successfully updated my project's typescript version from 4.4.4 to the latest, and the issue has been resolved.

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

Installation and execution of TypeScript jQuery / Bootstrap definition file on a local machine using npm typings: A step-by-step guide

Struggling to set up TypeScript jQuery and Bootstrap definition files in my new project using npm typings. Below are the steps I followed: 1- Open cmd, navigate to my project folder, and enter the following commands: npm install typings --global typings ...

The issue with APP_INITIALIZER is that it fails to resolve promises before moving on to other

I can't seem to figure out what's missing here. I hope it's just a minor issue. The problem I'm facing is that the APP_INITIALIZER isn't resolving completely. In my code, I have two services: AppSettingsService and SomethingServi ...

Error: Trying to access a property that is not declared on an empty object

Using a fully patched Visual Studio 2013, I am integrating JQuery, JQueryUI, JSRender, and TypeScript into my project. However, I am encountering an error in the ts file: Property 'fadeDiv' does not exist on type '{}'. While I believ ...

Leveraging an AngularJS service within Angular framework

I am trying to incorporate an AngularJS service into my Angular project. Below is my main.ts file: import {platformBrowserDynamic} from '@angular/platform-browser-dynamic'; import {AppModule} from './app/app.module'; import {UpgradeMo ...

TS-2304 Error - 'Iterable' not found in TypeScript when trying to import 'jquery' into a '.ts' file

Currently, I am utilizing TypeScript version 2.4 in Visual Studio Code for development. My approach involved installing jQuery via NPM using the given command: npm install --save @types/jquery Subsequently, I obtained the source code for the jquery modul ...

Issue accessing page from side menu in Ionic 2 application

I am experiencing an issue where the page does not open when I click on it in the side menu. Here is my app.component.ts file: this.pages = [ { title: 'NFC Page', component: NfcPage, note: 'NFC Page' }, ...

What is the method for launching Chrome synchronously in Selenium WebDriver using createSession()?

After executing the code below using Selenium WebDriver to launch a Chrome browser: import { Driver } from 'selenium-webdriver/chrome'; Driver.createSession(); console.log("I've launched!"); I'm encountering an issue where "I've ...

The modifications to the URL made by react-router-dom's 'useSearchParams' do not persist when adjusted through the onChange callback of the mui 'Tabs' component

One feature I am looking to implement is a tab navigation component that changes based on a specific search parameter called tab. For instance, if my URL reads as example.com?tab=test2, I want the navigation bar to highlight the item labeled test2. To ac ...

Issue encountered when attempting to assign a local variable as the initial value of an enum object member

Check out this playground link for a TypeScript example. I'm having issues setting the initial value of an enum member using a constant numeric value. Unfortunately, all subsequent values give an error stating "Enum member must have initializer." Is ...

Two services declared with "providedIn: 'root'" that have identical names

Imagine if there are two distinct services in two separate project categories, both sharing the same name. /app/services/category1/my.service.ts: @Injectable({ providedIn: 'root' }) export class MyService { foo() { return 'foo&apo ...

What allows the execution of "T[keyof T] extends Function" in TypeScript specifically for Strings?

Lately, I've been experimenting with type changes and I find myself puzzled when encountering code like the following: type DeepReadonly<T> = { readonly [k in keyof T]: T[k] extends Function?T[k]:DeepReadonly<T[k]> } // Let's defin ...

The combination of TypeScript decorators and Reflect metadata is a powerful tool for

Utilizing the property decorator Field, which adds its key to a fields Reflect metadata property: export function Field(): PropertyDecorator { return (target, key) => { const fields = Reflect.getMetadata('fields', target) || []; ...

Can you explain the significance of this particular method signature in the TypeScript code snippet shown above?

Referencing the ngrx example, we encounter the code snippet for the method store.select, which has a complex signature with two arrows. What is the significance of this method signature? The interface definition in the type file presents the following sig ...

Can you explain the meaning of '<Hero[]>' in programming jargon?

Hello there! I am new to learning angular and typescript, and currently going through a tutorial at angular. However, I stumbled upon something that I find confusing. For example: 1. getHeroes(): Observable<Hero[]> { this.messageService.add(&ap ...

The function "overloading" of the union type is not functioning properly

I attempted to "overload" a function by defining it as a union function type in order to have the type of the input parameter dictate the type of the `data` property in the returned object. However, this resulted in an error: type FN1 = (a: string) => { ...

Working with multiple observables in an array of properties using RXJS

I'm relatively new to using rxjs in my angular projects and I'm facing a challenge with a simple scenario. When making an http call to retrieve a group, it returns data including a list of "buddy ids", "genre ids", and a "region id". In order t ...

Form an object using elements of a string array

Trying to convert a string array into an object. The string array is as follows : let BaseArray = ['origin/develop', 'origin/master', 'toto/branch', 'tata/hello', 'tata/world']; I want the resulting obje ...

What could be causing my "Swiper" component to malfunction in a TypeScript React project?

In my React project, I decided to incorporate the Swiper library. With multiple movie elements that I want to swipe through, I began by importing it as follows: import Swiper from 'react-id-swiper'; Utilizing it in my code like this: <div cla ...

What is the proper way to display the date and time 2021-11-14T18:30:00.000+00:00?

Here is my ts file code: mydate: Date = new Date('2021-11-14T18:30:00.000+00:00'); However, I want the date to be in this format:- 07-July-2022 If anyone can assist with achieving this format, it would be greatly appreciated. Thank you! ...

limiting the number of HTTP requests within a JavaScript forEach loop

In my current coding situation, I am facing an issue where the HTTP requests are being made simultaneously within a forEach loop. This leads to all the requests firing off at once. const main = async () => { items.forEach(async (i: Item) => ...