What is the best way to loop through a template literal union type in JavaScript?

Is there a way to iterate over the different values in a string union type created with template literals?

type Foo = "Foo" | "Foo2" | "Foo3";

type Bar = "Bar" | "Bar2" | `${Foo}Type`;

One common approach is to use a <const> assertion, like this:

const Bar = ["Bar", "Bar2"] as const;
export type BarType = typeof Bar[number];

Bar.forEach((value) => {console.log(value)});

But trying to do the same with a template literal doesn't work because types aren't available at runtime.

const Bar = ["Bar", "Bar2", `${Foo}Type`] as const;

If that's not feasible, what would be the best alternative to achieve this goal?

How can I create a type based on another type that is usable both at compile time and runtime?

Answer №1

JavaScript values cannot be directly created from TypeScript types due to the fact that the static type system gets erased when TypeScript is compiled to JavaScript. Therefore, defining type Foo and type Bar will not allow you to create const Foo and const Bar. The workaround is to first create values and then use them to assist in creating types.

Creating the Foo type is relatively straightforward:

const Foo = ["Foo", "Foo2", "Foo3"] as const;
type Foo = typeof Foo[number];

The const assertion enables the compiler to recognize the literal types of the elements within Foo, allowing you to derive the union of those element types by indexing into Foo using a number key.


However, constructing the Bar type is more complicated. Here's a basic method to accomplish it:

const Bar = ["Bar", "Bar2", ...Foo.map(t => `${t}Type` as const)] as const;
console.log(Bar);
// const Bar: readonly ["Bar", "Bar2", ...("FooType" | "Foo2Type" | "Foo3Type")]

type Bar = typeof Bar[number];
// type Bar = "Bar" | "Bar2" | "FooType" | "Foo2Type" | "Foo3Type"

In this approach, the map() array method is utilized to add "Type" to every element of Foo.

It's important to note that the callback function is defined as t => `${t}Type` as const with its own const assertion. This instructs the compiler to determine what the template literal expression `${t}Type` evaluates to as a template literal type. Since t is inferred as type

Foo</code, <code>`${t}Type` as const
is inferred as type `${Foo}Type`, aligning with your intention.

While the TypeScript call signature for the map() method returns an unordered array type rather than a tuple type like that of

Foo</code, ordering isn't relevant in this scenario. Thus, the conclusion suffices for the task at hand.</p>
<p>Upon evaluation of the line <code>const Bar = ["Bar", "Bar2", ...Foo.map(✂) as const] as const
, another const assertion is made, ensuring that the compiler recognizes "Bar" and "Bar2". As a result, the type of the Bar value is determined as:

// const Bar: readonly ["Bar", "Bar2", ...("FooType" | "Foo2Type" | "Foo3Type")[]]

When querying the compiler for the element type by indexing into typeof Bar using a number, the desired outcome is achieved:

type Bar = typeof Bar[number];
// type Bar = "Bar" | "Bar2" | "FooType" | "Foo2Type" | "Foo3Type"

This establishes the intended structure of the Bar type.

Explore code on 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

Moving from Http to HttpClient in Angular4Changeover your Angular4

I recently migrated my Angular app to use the new HttpClient, but I'm encountering some challenges in achieving the same results as before with Http. Can anyone help me out? Here's what I was doing with Http: getAll() { return this.http.get ...

How can I restrict the return type of a generic method in TypeScript based on the argument type?

How can we constrain the return type of getStreamFor$(item: Item) based on the parameter type Item? The desired outcome is: When calling getStream$(Item.Car), the type of stream$ should be Observable<CarModel> When calling getStream$(Item.Animal), ...

effects of material ui buttons

I have implemented two material ui buttons on my page. <Button variant="contained">Apply</Button> <Button variant="contained">Remove</Button> I am looking to add some functionality where a description of what ea ...

Error: Angular 2 - Node - gulp | Unable to locate module .component

I'm in the process of developing a complete TypeScript application with Node.js in TypeScript that is intended to be used with Angular 2 and built using Gulp as the build tool. The Gulp task successfully compiles all files from /src to /dist, convert ...

How to access elements by their class name in Angular

Recently, I encountered a situation with this specific span element: <span *ngFor="let list of lists[0].question; let i = index" id="word{{ i }}" (click)="changestyle($event)" class="highlight"> {{ list}} < ...

Utilize decorators for enhancing interface properties with metadata information

Can decorators be utilized to add custom information to specific properties within an interface? An example can help clarify this: Interface for App state: export interface AppState { @persist userData: UserData, @persist selectedCompany: UserCo ...

Tips for Implementing CdvPurchase.Store in Angular

Is there a way to configure cordova-plugin-purchase v13 in conjunction with Angular 15? Here is a snippet of what I have attempted. Below is my PaymentService class that is set to be provided at the root level. // payment.service.ts import { Injectable } ...

ngFor filter based on user input

I am working on a 2-step stepper feature where I need to filter the values in my amountArray based on the age of the person. If the person is above 50 years old, display only the values 10000 and 15000. For Euro currency, show values 25000 and 50000. I att ...

Defining initial prop values in unit tests with React Testing Library

I'm currently streamlining my unit tests in React Testing Library by creating a reusable function to render components with specified props. This helps minimize code repetition and allows me to easily change props for each test. However, I've enc ...

When utilizing Google Analytics in conjunction with Next.Js, encountering the error message "window.gtag is not

Encountering an error on page load with the message: window.gtag is not a function Using Next.js version 14.0.4. All existing solutions seem to hinder data collection, preventing the website from setting cookie consent correctly. I am uncertain about the ...

Applying Validators manually in Angular 2 beta 17

We are currently working on a legacy project that needs to be maintained until the final version with angular-final is deployed. Once we upgrade to the final version, I will be able to easily apply conditional Validators using: this.myForm.controls[&apos ...

Angular 2 Custom Pipe Magic

I'm brand new to using the Angular 2 framework and I'm attempting to create a custom filter. app.component.ts import {Component} from 'angular2/core'; import {HTTP_PROVIDERS} from 'angular2/http'; @Component({ selector: ...

Utilizing Typescript generics with an optional second parameter

Learning about generics in typescript has been quite challenging for me. However, I was able to make it work successfully. export type Events = { LOGIN: undefined NAVIGATION: { screen: string } SUPPORT: { communication_method: 'chat&ap ...

Broaden the `...args: Parameters<T>` to enable the inclusion of additional optional parameters towards the end

I am currently working on a function that can take a function as input and return an "extended" function. The returned function should have the same parameters as the original function, with an additional optional parameter at the end. I have managed to ...

TypeScript Add Extract Kind

I am currently working on implementing a function called sumPluck. This function will allow the user to specify a property of type number from an object in an array, and then calculate the sum of all those properties. For example: type A = { prop: number ...

What is the method for throwing errors with NestJS guards without relying on an authentication module?

Are you looking to customize error responses in NestJS guards? import { CanActivate, Injectable, ExecutionContext, NotFoundException } from '@nestjs/common'; import { Observable } from 'rxjs'; import { InjectModel } from '@nestjs/m ...

Looking for an instance of a node.js ftp server?

I'm facing a challenge in creating a node.js application that can establish a connection with an FTP server to download files from a specific directory: Despite attempting to follow the instructions provided in the documentation for the ftp npm packa ...

The component is expected to return a JSX.Element, however it is failing to return any value

The issue lies with this component: const NavigationItems = (props: {name: string, href: string}[]): JSX.Element => { props.map((item, index) => { return <a href={item.href} key={index}>{item.name}</a> }) }; export default Naviga ...

Convert numeric month to its 3-letter abbreviation

Receiving the date time value from the server and storing it in a variable named a1: let a1 = (new Date(estimatedServerTimeMs)); console.log of a1 Sun Apr 05 2020 11:36:56 GMT+0530 (India Standard Time) The date is converted to a simpler format such as ...

I'm encountering a 502 error while trying to use Supabase's signInWIthPassword feature

Despite all authentication functions working smoothly in my React, TypeScript, and Supabase setup, I'm facing an issue with signInWithPassword. In my context: I can successfully signIn, create a profile, and perform other operations like getUser() an ...