Limit potential property values based on the existing keys within the object

My structure looks like this:

export interface AppConfig {
  encryptionKey: string;
  db: TypeOrmModuleOptions;
}

export interface BrandsConfig {
  /**
   * Brand name
   */
  [key: string]: AppConfig;
}

export interface IConfig {
  brands: BrandsConfig;
  master: string;
}

export class Config implements IConfig {
  public readonly brands: BrandsConfig;
  public readonly master: string;
  public constructor(init: IConfig) {
    Object.assign(this, init);
  }
}

The concept behind BrandsConfig is to be a collection of key/value pairs, where each key represents a brand name and the value is a setting specific to that brand. On the other hand, "master" is intended to designate one instance with a special status.

I am exploring a way to statically constrain the possible values of "master", ensuring that it aligns with the keys available in BrandsConfig. While I could perform this validation at runtime in the constructor, my aim is to shift such checks to the compilation phase for enhanced intellisense support. Is there a method to achieve this, potentially at the interface level as well as within the Config class?

Answer №1

If you want to achieve this, utilize Generics along with the keyof/Index Type Query operator.

1.) Start by introducing a generic type parameter B to your Config class. This parameter should be assignable to or extend BrandConfig:

export class Config<B extends BrandsConfig> implements IConfig<B> {
  public readonly brands: B;

  public readonly master: keyof B;

  public constructor(init: IConfig<B>) {
    // Ensure it compiles. You can modify it as needed.
    this.brands = init.brands;
    this.master = init.master;
  }
}

2.) Additionally, add a corresponding generic type parameter to IConfig so that we can define master as a key dependent on keys in BrandsConfig:

export interface IConfig<B extends BrandsConfig> {
  brands: B;
  master: keyof B;
}

Test this on the client side. If an incorrect key is provided for master, an error is expected.

const myConfig = {
  brands: {
    brand1: { encryptionKey: "", db: {} },
    brand2: { encryptionKey: "", db: {} }
  },
  master: "brand1"
} as const

const config = new Config(myConfig); // This works!

If you change the master property to master: "Wanna be a brand", you will encounter:

Type '"Wanna be a brand"' is not assignable to type '"brand1" | "brand2"'.

Also, note the use of const assertions to narrow down the inferred type of myConfig.

Playground

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 is the TypeScript's alternative to ReasonML's option type?

When using ReasonML, the option type is a variant that can be either Some('a) or None. If I were to represent the same concept in TypeScript, how would I go about it? ...

Having trouble compiling Typescript code when attempting to apply material-ui withStyles function

I have the following dependencies: "@material-ui/core": "3.5.1", "react": "16.4.0", "typescript": "2.6.1" Currently, I am attempting to recreate the material-ui demo for SimpleListMenu. However, I am encountering one final compile error that is proving ...

At what juncture is the TypeScript compiler commonly used to generate JavaScript code?

Is typescript primarily used as a pre-code deployment tool or run-time tool in its typical applications? If it's a run-time tool, is the compiling done on the client side (which seems unlikely because of the need to send down the compiler) or on the s ...

Promise<IDropdownOption[]> converted to <IDropdownOption[]>

I wrote a function to retrieve field values from my SPFx list: async getFieldOptions(){ const optionDrop: IDropdownOption[]= []; const variable: IEleccion = await sp.web.lists.getByTitle("Groups").fields.getByTitle("Sector").get ...

What is the process for defining the default landing page route in Angular routing?

My application currently has only one route, and I want it to start with the URL http://localhost:4200/specialtyQ. However, my application is not loading properly. The code snippet below is what I am using to try to achieve this. How can I correct the URL ...

The parameter 'To' cannot be assigned the argument of type '{pathname: string; shipment: TShipment;}'

Is there anyone who can assist me with a Typescript issue I'm currently facing? Upon running tsc in my project, I encountered the following error: I am getting the error saying that 'Argument of type '{ pathname: string; item: Item; }' ...

Switching out a traditional class component with a functional component within a hook to deduce properties from T

One challenge is to subtract props from T within the withHookFn function using a function instead of a class as successfully done in the withHook function. The code contains comments explaining this issue. Dive into the code for more insights. import Reac ...

Sending asynchronous data to a child component in Angular 2

Having trouble with passing asynchronous data to a child component. I am attempting to create a dynamic form generator, but I encounter an issue when trying to fetch JSON data via an Observable and then passing it to the child component. Service: generat ...

Error: The specified function in the schema is not valid for the current operation mode

I'm facing an issue with validating a material ui form using Formik and Yup. The error keeps popping up. This is the schema I imported from another file: export const validationSchema = Yup.object({ email: Yup.string() .email('Invalid Ema ...

Guide to updating the content of an input field

As a newcomer hobbyist, I'm attempting to automate my web browsing experience. My goal is to have the browser automatically fill in my username and password, and then click the sign-in button using a combination of JavaScript and Tampermonkey. This me ...

Executing a series of API calls using Rxjs in Angular that result in a null response

I encountered a situation where I needed to make sequential API calls using RxJs in Angular. However, despite successfully implementing the calls, I am struggling with a null error. In this scenario, the second API call depends on receiving an id from the ...

How can Angular developers properly implement token refreshing in their applications?

Recently, I've been struggling with implementing a logic in my code. I have a specific requirement: Whenever there is a signed request (signed - means it has a JWT token for authenticated users) made to the API backend, the API backend may respond w ...

The error message "Undefined error in Angular 8 when waiting for API call to finish" appears when

if(this.datashare.selectedtableId!=null) { console.log( "inside if condition"); let resp= this.http.get(this.global.apiUrl+"columns/"+this.datashare.selectedtableId); resp.subscribe((data)=>this.users=data); conso ...

Encountering an issue with the 'createObjectURL' function in URL, resulting in overload resolution failure when using npm file-saver

While working on my angular app, I encountered a situation where I needed to download user details uploaded as a Word document to my local machine using the angular app. Successfully, I was able to upload and save this data to my database, getting its byte ...

Analyzing different kinds of inputs received by a function

Let's say we have the following abstractions for fetching data from an API: Data storage class class DataItem<T> { data?: T | null } Query function function queryData ( fn: () => Promise<any> item: DataItem<any> ...

To allow users to sign in, the mutation can be filtered based on the boolean value of `isVerified

How can I filter and only allow users with isVerified === true to sign in? If it's false, the user should not be able to sign in through the mutation. Here is my code for the mutation: signin: async ( _: any, { credentials }: SignInArgs, ...

Exploring Apollo Client's invalidateQueries feature

React Query provides a functionality called invalidateQueries which allows us to designate cached data as outdated and triggers a refetch of related queries. Is there a similar feature available in Apollo Client? I currently have a list of entities that I ...

A guide on passing an ngFor object variable to a function

I am trying to display subcategories for each category in my *ngFor list. The subcategory data is retrieved from Firebase using the category_id, but I am struggling to pass the category_id from the HTML to the function for every member of the category. ho ...

Displaying updated information in Angular

I recently developed a chat application using Angular that utilizes the stomp socket from @stomp/ng2-stompjs. To display all messages, I am leveraging *ngFor. <p *ngFor="let item of messages" style="padding: 5px; font-size: 18px"> <span style ...

Trigger ng-bootstrap modal programmatically

I have an Angular 4 page with a ng-bootstrap modal. My code is shown below. foo.html [...] <button class="btn btn-sm btn-secondary material-icons" (click)="open(content)">search</button> <ng-template #content let-c="close" let-d="dismiss" ...