0

There is an enum for process. They have different steps, which are also represented as enums as shown below.

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

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

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

With the following statements, I created an array of steps.

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
};

I wrote the following function to get the step number.

// ???: 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, which is correct
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));

As you can see in the code example, is it possible to prevent calling the function with wrong step during compile-time using generics?

Here is the playground, if you wish to try out the whole example: TS Playground

1 Answer 1

4

One option is to introduce a conditional type that will allow you to infer the require step enum (i.e. SimpleStep or AdvancedStep) based on the Process supplied to the function. This could be done as follows:

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

Then you could change your function to use that type:

const getStepNumber = <P extends Process>(process: P, step: StepFromProcess<P>) => ...

The compiler will now prevent you making this (invalid) call:

console.log('step number of C', getStepNumber(Process.Simple, AdvancedStep.C));
Sign up to request clarification or add additional context in comments.

2 Comments

Wow, that's really cool. Thanks. If there are more than 2 processes, is there switch-case like typing? Or should it be done with ternary conditional expression?
No, I don't believe there is a switch-case. You need to use nested ternary operators. You can see an example here in the TypeScript docs (look at their TypeName example there).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.