Checking for enum type in TypeScript generics

In this scenario, there is an enum called Process which consists of different steps represented by enums such as SimpleStep and AdvancedStep.

enum Process {
  Simple = "simple",
  Advanced = "advanced"
}

enum SimpleStep {
  A = "A",
  B = "B"
}

enum AdvancedStep {
  A = "A",
  B = "B",
  C = "C"
}

To create an array of steps using the provided statements:

const SIMPLE_STEPS = Object.keys(SimpleStep).map(
  (k: string) => SimpleStep[k]
);

const ADVANCED_STEPS = Object.keys(AdvancedStep).map(
  k => AdvancedStep[k]
);

const ALL_STEPS = {
  [Process.Simple]: SIMPLE_STEPS,
  [Process.Advanced]: ADVANCED_STEPS
};

A function was written to determine the step number based on the process used.

// ???: Check if S is a step of Process
const getStepNumber = <P extends Process, S>(process: P, step: S) => {
  return ALL_STEPS[process].indexOf(step) + 1;
};

// returns 2, correct result
console.log('step number of B', getStepNumber(Process.Advanced, AdvancedStep.B)); 

// returns 0. Is it possible to prevent at compile-time?
console.log('step number of C', getStepNumber(Process.Simple, AdvancedStep.C));

The code sample begs the question of whether it is feasible to use generics to safeguard against calling the function with incorrect steps at compile time.

If you want to experiment with the complete example, feel free to access the playground through this link: TS Playground

Answer №1

To improve the code, consider implementing a conditional type that enables the inference of the necessary step enum (either SimpleStep or AdvancedStep) based on the provided Process. Here's how this can be achieved:

type DetermineStepFromProcess<P extends Process> =
    P extends Process.Simple ? SimpleStep : AdvancedStep

Next, update your function to utilize this type:

const determineStepNumber = <P extends Process>(process: P, step: DetermineStepFromProcess<P>) => ...

By making these changes, the compiler will now catch and prevent you from making an erroneous call like:

console.log('step number of C', determineStepNumber(Process.Simple, AdvancedStep.C));

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

The component 'Form' cannot be utilized in JSX because its return type, 'ReactNode', is not a valid JSX element

I'm facing an issue with my Next.js application written in TypeScript after updating react-bootstrap. After the update, I am encountering the following error when attempting to use the Form component from react-bootstrap: react-bootstrap: ^2.10.3 @typ ...

Tips for leveraging stage 3 functionalities in TypeScript?

Array.prototype.at() is currently in the proposal stage 3. Even after adding "lib": ["ESNext"] to my tsconfig.json, I encountered the error: Property 'at' does not exist on type 'number[]'. Could you shed some light ...

What is the initial component that Angular will activate or run?

Uncertainty surrounds the correctness of my question. Within my application, there exist numerous routing components and feature modules. I am eager to discern whether the appcomponent.ts file or the routing logincomponent.ts file will be executed first. ...

Execute the CountUp function when the element becomes visible

Currently, I am implementing the following library: https://github.com/inorganik/ngx-countUp Is there a way to activate the counting animation only when the section of numbers is reached? In other words, can the count be triggered (<h1 [countUp]="345 ...

Issue with binding nested ViewModels/components in Knockoutjs using TypeScript does not resolve

Struggling with implementing a viewModel within another viewModel in knockout. Any assistance would be greatly appreciated. Using typescript and aiming to have a list of address controls, each with their individual viewmodel. Initially, the project functi ...

the ad grid fails to load properly on the page

In the midst of developing my angular 4 project, which is based on a starter template found here, I encountered an issue while trying to integrate ag-grid into my project. Following the instructions provided here, I successfully created a new example proj ...

"HandsOnTable, showcasing its capability to effortlessly load data containing formulas

I am working with a HandsonTable that receives data in the form of an array of arrays. After applying some formulas and saving the data, I am now facing an issue while trying to load the data directly into the HandsonTable component in ReactJs. The problem ...

Having trouble with i18n types not functioning correctly in Typescript and Nuxt?

I am currently utilizing NuxtJS/i18n with TypeScript and have included its types in my TS config: { "compilerOptions": { "types": [ "@nuxt/types", "@nuxtjs/i18n", ] } } However, when attempti ...

Problem encountered with the onSuccess callback in the Next.js useQuery hook caused by tprc.authCallback

Encountering an issue with the onSuccess callback and useQuery hook from trpc.authCallback in a Next.js app. Despite defining the success parameter type, TypeScript throws an error claiming onSuccess is not found in provided options. My code snippet: impo ...

How can you funnel the output from TSC into Rollup without TSC generating any files in the process?

My current setup involves using rollup with TSC to create ES modules based bundles. When I trigger the command npm run build, TSC transpiles the .ts files into .js files within the src directory. Subsequently, Rollup performs certain optimizations such as ...

Tips for building an interactive button with changing content and retrieving variable information when the button is clicked in an Angular framework

Received dynamic data from the server is shown below: { "data": [ { "id": 4, "first_name": "Eve", "last_name": "Holt", "lat":"25.6599899", "lng":"45.3664646", "status":"0" ...

The specific property 'splice' cannot be found within type 'T'

As I delve into working with TypeScript, an unexpected error arises: Property 'splice' does not exist on type 'T'. type Item = { name: string, body: string, imgOne: string, imgTwo: string, }[] // Another file contains this func ...

Different property 'responseType' types are not compatible

Having trouble making a POST request in Angular 5 that accepts text/plain as a response. The method being called in Angular expects a JSON response, causing an error when trying to parse the response. Attempted to call the method with parameter {responseT ...

Code coverage analysis in a node.js TypeScript project consistently shows no coverage metrics

I'm currently working on a backend TypeScript project where I'm aiming to obtain coverage reports for unit test cases. However, Jest is returning empty coverage reports both in the terminal and in the HTML report, with no information provided. Ev ...

Setting up a global CSS and SASS stylesheet for webpack, TypeScript, Phaser, and Angular: A step-by-step guide

A manual configuration has been set up to accommodate all the technologies mentioned in the title (webpack, typescript, phaser, and angular). While it works perfectly for angular component stylesheets, there seems to be an issue with including a global st ...

Accessing video durations in Angular 2

Can anyone help me with retrieving the video duration from a list of videos displayed in a table? I attempted to access it using @ViewChildren and succeeded until encountering one obstacle. Although I was able to obtain the query list, when attempting to a ...

Having trouble getting Angular 8 WebRTC to function properly on two tabs

I've been tasked with creating an audio chat room for 2 users. Initially, I used the following app example: Peer connection: audio only After converting the code to TypeScript, it successfully ran: Stackblitz However, I'm facing challenges ge ...

What causes the cursor in an editable div to automatically move to the front of the div?

<div className="min-w-[600px] min-h-[36.8px]" > <div id={`editableDiv-${Object.keys(item)}-${index}`} className="p-3" contentEditable suppressContentEditableWarning onInput={(e) => onChange(e)} > ...

What is the method for utilizing a class variable without assigning a value to it initially

class testClass { student: { name: string, age: number } constructor(options?: any) { this.student = { name: '', age: 0 }; } setStudent(name:string, age:number) { this.student.name ...

Limit input to numbers only in Semantic UI React Form Field

I've developed a custom CurrencyInput React component for users to input numeric values. I set the input type to "number", but unfortunately, this only seems to function properly in Chrome (as Firefox still allows non-numeric entries). Here is the co ...