Generate an instance containing attributes that correspond to constant string values within a class

In the world of TypeScript, I have a scenario that might be a bit tricky, but I'll throw it out there anyway.

Let's say I start with a base service class:

export default class Service {
  public static readonly accessorName: string

  constructor(protected prisma: PrismaClient, protected services: ServiceMap) {}
}

Now, imagine I have multiple classes that extend this base class:

export default class Users extends Service {
  public static readonly accessorName = "users"
}

export default class Warehouses extends Service {
  public static readonly accessorName = "warehouses"
}

export default class Depots extends Service {
  public static readonly accessorName = "depots"
}

My goal is to create a statically typed object structured like this:

type ServiceMap = {
  users: Users
  warehouses: Warehouses
  depots: Depots
}

Note that each key in ServiceMap should correspond to the accessorName property from each class.

I've tried various approaches and got close by organizing the classes in a module and exporting them:

import Users from "./Users"
import Warehouses from "./Warehouses"
import Depots from "./Depots"

export { Users, Warehouses, Depots }

Then, I created a separate ServiceMap module that imports these services and defines them under a namespace.

import { PrismaClient } from "@prisma/client"
import * as services from "./services"
import { Service } from "./services/Service"

type Services = typeof services

export type ServiceMap = {
  [S in keyof Services]: InstanceType<Services[S]>
}

export default function createServiceMap(prisma: PrismaClient) {
  const map: Partial<ServiceMap> = {}

  Object.values(services).map(
    (s: typeof Service) => (map[s.name as keyof ServiceMap] = new s(prisma, map as ServiceMap))
  )

  return map as ServiceMap
}

However, the resulting type for ServiceMap turns out to be:

type ServiceMap = {
  Users: Users
  Warehouses: Warehouses
  Depots: Depots
}

Furthermore, using map[s.name as keyof ServiceMap] triggers an error message saying:

Type 'Service' is not assignable to type 'Users & Warehouses & Depots'.

If feasible, my ultimate aim is to utilize the accessorName attribute for generating the keys within the ServiceMap. Is there a way to achieve this?

Answer №1

If you're looking for the key renaming feature in mapped types, here's an example:

type Foo = { a: string, b: string }
type Bar = { [K in keyof Foo as `get${Uppercase<K>}`]: Foo[K] }
// Bar is { getA: string, getB: string }

In your situation, you can apply the same concept but focus on accessorName.

const services = { Users, Warehouses, Depots }
type Services = typeof services

type ServiceMap = {
  [S in keyof Services as Services[S]['accessorName']]: InstanceType<Services[S]>
}

This setup will allow you to do the following:

function createServiceMap(prisma: PrismaClient) {
  const map: Partial<ServiceMap> = {}
  // TODO: implementation
  return map as ServiceMap
}

const map = createServiceMap(prisma)
map.depots // Depots

Check out the Playground demo

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

Exploring the TypeScript handbook: Utilizing rootDirs for Virtual Directories

Exploring the concept of Virtual Directories with rootDirs in the handbook, we find an interesting example demonstrating how modules can be imported from different source folders by combining multiple rootDirs. // File Structure src └── views ...

Extract a string value from a TypeScript enum

Here is a basic enum definition: export enum Type { TEST_ONE = "testing.one", TEST_TWO = "testing.two", BETA = "beta.one" } I am looking to run a function for each string value in the enum. For example: executeType(type: string) { console.lo ...

Retrieving data from a form input that utilizes reactive checkboxes

Hey there, I am currently working on implementing a Reactive Form and facing an issue with fetching values from checkboxes. It seems that only the value of the first checkbox selected is being recognized while the others are not. Below is the snippet of my ...

Issue with handsontable numbro library occurs exclusively in the production build

Encountering an error while attempting to add a row to my handsontable instance: core.js.pre-build-optimizer.js:15724 ERROR RangeError: toFixed() digits argument must be between 0 and 100 at Number.toFixed () at h (numbro.min.js.pre-build-op ...

In TypeScript with React, utilizing ref to access the video element and trigger the .play() method

I have a TypeScript React video component that I want to play when clicked. My approach is to use a click handler that accesses the video through a ref and calls .play(), but I keep encountering this error: TS2339: Property 'play' does not exist ...

Utilizing Typescript to define key-value pairs within a structured form

I've been creating a form structure that's functioning smoothly, but I've encountered an interesting issue with field validation in the validation part. While my Visual Code is pointing out that 'required2' in the phone constant n ...

The return type of a getter is `any` if the object contains a method and is processed by a generic function

I am facing an issue with my code where the getter's return type is set to any, even though the actual return type should be clear. There are certain additional functions triggering this behavior: // This is necessary for reproduction const wrapperFun ...

What could be the reason for the TypeScript compiler not recognizing tsconfig.json?

I recently came across a file from a tutorial that has left me confused due to the inconsistency between documentation, tutorials, and examples: /scripts/tsconfig.json: { "compilerOptions": { "emitDecoratorMetadata": true, "experiment ...

I'm having trouble with my code not working for get, set, and post requests. What could be causing this issue and how can I

Here are the TypeScript codes I've written to retrieve product details and delete them. import { Component, OnInit } from '@angular/core'; import {FormGroup,FormBuilder, FormControl, Validators} from "@angular/forms" // other impor ...

Angular: How to Disable Checkbox

Within my table, there is a column that consists solely of checkboxes as values. Using a for loop, I have populated all values into the table. What I have accomplished so far is that when a checkbox is enabled, a message saying "hey" appears. However, if m ...

Utilizing TypeScript's higher-order components to exclude a React property when implementing them

Trying to create a higher-order component in TypeScript that takes a React component class, wraps it, and returns a type with one of the declared properties omitted. Here's an attempt: interface MyProps { hello: string; world: number; } interfac ...

The Formik and React error is indicating that the '{ refetch: any; }' type is absent

When attempting to pass a prop down to my EmailSignupScreen, I encountered an error message. This issue arose while experimenting with Formik and Typescript. "message": "Type '{ refetch: any; }' is missing the following properties from type &apo ...

Creating a comprehensive object within a single interface using Typescript

Here is an example of an Object in TypeScript: export class test{ recordname: string; comments: [{ comment: string }] } To define it using one interface instead of multiple interfaces, you can try something like this: export int ...

Having trouble locating the bootstrap import statement

Currently, I am working on a project in Angular where I have defined two styles in the angular.json file - styles.css and node_modules/bootstrap/dist/css/bootstrap.min.css. After running ng serve, it shows that it compiled successfully. However, upon ins ...

Ionic storage is unable to assign a number as a string

My goal is to store all numbers retrieved from the function getWarrentsNumber() in ionic storage, but I encountered an error. Error: The argument of type "number" cannot be assigned to type 'string'. this.storage.set(this.NumberOfAssignedWarren ...

Mocking multiple services and their constructors in an Angular 2 TypeScript Jasmine test component

I've got this login component code snippet that I need help testing in Angular. import { Component, OnInit } from '@angular/core'; import { Router } from '@angular/router'; import { FormBuilder, FormGroup, Validators } from '@ ...

Search for an element deep within a tree structure, and once found, retrieve the object along with the specific path leading to

I created a recursive function to search for a specific object and its path within a tree structure. However, when I changed the target ID (from 7 to 10) in the function, I encountered an error: "message": "Uncaught TypeError: Cannot read ...

The concept of type literals in Typescript provides a powerful tool for achieving function

In TypeScript, I am aiming to create an overloaded function with named parameters. Despite the code running correctly, I encounter warnings about `init.index` potentially not existing in one of the function signatures. The purpose of overloading is to off ...

Preventing parent requests from being triggered when a child element is clicked in Angular 2

I have a similar code structure as shown below and I am trying to achieve the behavior where clicking on the Child Div does not trigger the Parent Div click event. <div class="parentDiv" (click)="parentDiv()"> <div class="childDiv" (click)="ch ...

Can anyone guide me on implementing getServerSideProps in a TypeScript NextPage component?

I've come across a page that I'd like to replicate, with the code sourced from https://github.com/dabit3/nextjs-lit-token-gating/blob/main/pages/protected.js: import Cookies from 'cookies' import LitJsSdk from 'lit-js-sdk' ex ...