Creating a new interface object in Typescript by utilizing the keyof operator to reference another interface, while also modifying the key name through string concatenation

Scenario: I am dealing with multiple select boxes filled with various select options.

Here are the different types that I am working with:

type Option<T = any> = {
    value: T
    // reserved for option
    [prop: string]: any
}

export type OptionType<T = any> = Option<T>

interface SelectBox {
    affiliate_id: number
    campaign_id: string
    username: number | string | undefined
}

interface SelectBoxOptions {
    affiliate_id_options: OptionType<number>[]
    campaign_id_options: OptionType<string>[]
    username_options: OptionType<number | string | undefined>[]
}

Is there a way to automate the creation of the SelectBoxOptions type, considering it follows a consistent pattern?

I attempted the following approach:

export type SelectBoxOptions<T> = {
    [K in keyof T as `${K}_options`]: OptionType<T[K]>[]
}

An example use case is demonstrated below:

//
//     interface NumberFilter {
//         affiliate_id: number
//         campaign_id: string
//     }
//
//     SelectBoxOptions<NumberFilter> == {
//         affiliate_id_options: OptionType<number>[]
//         campaign_id_options: OptionType<string>[]
//     }
//

However, I encountered this error from tsc:

https://i.sstatic.net/IrHRv.png


app/javascript/app/services/FilterService.ts:51:25 - error TS2322: Type 'K' is not assignable to type 'string | number | bigint | boolean'.
  Type 'keyof T' is not assignable to type 'string | number | bigint | boolean'.
    Type 'string | number | symbol' is not assignable to type 'string | number | bigint | boolean'.
      Type 'symbol' is not assignable to type 'string | number | bigint | boolean'.

51     [K in keyof T as `${K}_options`]: OptionType<T[K]>[]

I am puzzled about why this generic type is causing an error. It appears correctly on hover in VSCode when I employ it...

If anyone knows how to prevent the error on "K" in ${K}_options, please share!

Answer №1

Symbols cannot be converted to strings, which means they cannot be used in template literal types.

To filter out other types like numbers and symbols, intersect keyof T with string:

type Option<T = any> = {
  value: T;
  // reserved for option
  [prop: string]: any;
};

export type OptionType<T = any> = Option<T>;

interface SelectBox {
  affiliate_id: number;
  campaign_id: string;
  username: number | string | undefined;
}

export type SelectBoxOptions<T> = {
  [K in keyof T & string as `${K}_options`]: OptionType<T[K]>[];
};

interface NumberFilter {
  affiliate_id: number;
  campaign_id: string;
}

type X = SelectBoxOptions<NumberFilter>;

Playground


When T is a tuple, keys are automatically given as strings like "0", "1", "2", etc. Therefore, intersecting with only string is adequate.

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

Encountering ng build --prod errors following Angular2 to Angular4 upgrade

Upon completing the upgrade of my Angular2 project to Angular4 by executing the following command: npm install @angular/common@latest @angular/compiler@latest @angular/compiler-cli@latest @angular/core@latest @angular/forms@latest @angular/http@latest @an ...

Choose a Superclass or Interface for your Subclass

I currently have the following setup: class Dog extends Animal{} class Animal{} This is a simple inheritance structure. Now, I have another class that contains all types of animals. class Farm{ animals: Animal[]; } At a certain point in my applic ...

The assignment of `accessToken` is restricted in Mapbox-gl's typing

I'm currently utilizing the mapbox-gl library in conjunction with TypeScript. Moreover, I have successfully installed its type definitions that are sourced from the community using @types/mapbox-gl. However, when attempting to import and assign an acc ...

Here is a unique spin on the topic: "Strategies for dynamically updating the error

I created a demo on stackblitz that can be viewed at this link. The demo focuses on email validation, however there seems to be a bug in the code related to the error message stating "Invalid email ID". This error message is still displayed even after rem ...

Is it possible to access the generic type that a different generic type inherits in TypeScript?

I've developed an interface specifically designed for types capable of self-converting to IDBKey: interface IDBValidKeyConvertible<TConvertedDBValidKey extends IDBValidKey> { convertToIDBValidKey: () => TConvertedDBValidKey; } My goal n ...

Encountering an Eslint issue: "Function missing return type" while adding a styled component to _document.tsx in Next.js

Setting up my NextJS project with styled components and Typescript has been my current focus. After consulting the official NextJS documentation, I successfully configured the _document.tsx file, which appears like this: import Document, { DocumentContext ...

Using Angular, Typescript, and ngxs to manage state observables, one may wonder what exactly a variable ending with an exclamation mark (!) signifies. An example of this can be seen in the following code snippet:

Within my TS file, a declaration is present: import { Select } from '@ngxs/store'; @Injectable() export class someService { @Select(someSELECTOR) varName$!: Observable<someType[]>; elements$ = this.varName$.pipe( map(elements => e ...

TypeScript encountered an error with code TS2305, stating that the module "constants" does not have any exported members

My Vite + React + TypeScript application has the following structure: src constants a.ts b.ts index.ts components Comp.tsx tsconfig file with "baseUrl": "src" The content of a.ts is as follows: export const ARRAY = ...

How to trigger a component programmatically in Angular 6

Whenever I hover over an <li> tag, I want to trigger a function that will execute a detailed component. findId(id:number){ console.log(id) } While this function is executing, it should send the id to the following component: export class ...

What is the reason behind facing TS errors locally after pulling a PR and not on CI builds?

As I delve into e2e testing in Cypress using TypeScript, I am puzzled by the TS errors that are cropping up locally while everything runs smoothly on CI. The peculiar thing is that these TS errors only started appearing after checking out a particular PR, ...

Using TypeScript to type styled-system props

Currently, I am utilizing styled-system and one of the main features of this library is its shorthand props that allow for simple and quick theming. Although I have streamlined my component, a significant aspect lies here: import React from 'react&a ...

Associate a fresh entity with an Angular form rather than individual attributes

My interface is quite simple: (models.ts) export interface User{ Id : number; FirstName : string; LastName : string; Email: string; PhoneNumber: string; } As for my form, it's also pretty straightforward: (app.component.html) <fieldset class ...

Angular Material: Enable Window Dragging Across Multiple Monitors

Exploring the usage of Angular Material Dialog or any other Popup Window Component. The functionality is mostly working as expected, with the exception of the last point. a) The original screen should not have a grey overlay, b) Users should be able to i ...

Using Typescript to override an abstract method that has a void return type

abstract class Base{ abstract sayHello(): void; } class Child extends Base{ sayHello() { return 123; } } The Abstract method in this code snippet has a return type of void, but the implementation in the Child class returns a number. S ...

Troubleshooting: When attempting to integrate a Flask Backend with a NextJS frontend, encountering issues with files not being received properly

I have developed a Flask Backend with the purpose of accepting and processing files. Recently, I created a Next.js frontend where users can upload files. Strangely enough, when I followed the same steps as in my Flask project using a template HTML file, it ...

Issues with eventEmitter functionality in Angular 2

Everyone performed admirably following the manual, here is the code snippet for WebSocketBroadcaster: import {EventEmitter, Injectable} from "@angular/core"; @Injectable() export class WebSocketBroadcaster { ee: EventEmitter<any> = new EventEmi ...

Differences Between Angular 2 Reactive Forms and Template Forms

We are embarking on a new Angular 2 project and are deliberating on whether to opt for Reactive Forms or Template Forms. If you want to learn more, you can refer to this article: https://angular.io/guide/reactive-forms From what I understand, the main adv ...

Retrieve the specific type of property from a generic data structure

I am currently working on a project where I need to determine the type of property within a given Type: type FooBarType { foo: string, bar: number } The function would be structured like this: getType<K extends keyof T>(key: K): string. The ...

Why is it that retrieving the same class data from two separate TypeScript files yields varying results in TypeScript? What steps can be taken to resolve this issue

I have created 3 TypeScript files named dataService.ts, init.ts, and interest.ts. The dataService.ts file is a common file used to store and retrieve data from the other 2 files. Within the dataService.ts file: export default class DataService { priv ...

The function does not throw a compiler error when a parameter is missing

Currently utilizing TSC Version 2.4.2 Please take note of the following interface: interface CallbackWithNameParameter { cb: (name: string) => void } This code snippet: const aCallback: CallbackWithNameParameter = { cb: () => {} }; Manages t ...