I will simplify the example a bit, but the underlying issues remain the same.
In TypeScript, there are enums where each enum value is defined as either a string literal or a numeric literal type. These types of enums act similarly to union types, which can be narrowed down using type guards. This type of enum is known as a union enum:
// union enum
const enum SupportedColor {
BLUE = "Blue",
YELLOW = "Yellow"
}
// no error here
function doSomething(supportedColor: SupportedColor): boolean {
switch (supportedColor) {
case SupportedColor.BLUE:
supportedColor // SupportedColor.BLUE
return true;
case SupportedColor.YELLOW:
supportedColor // SupportedColor.YELLOW
return true;
}
}
In the above code, the compiler identifies that the switch statement covers all possible cases because the type of 'supportedColor' can be narrowed based on control flow analysis. The type SupportedColor represents the union SupportedColor.BLUE | SupportedColor.YELLOW. Therefore, within each case block, 'supportedColor' gets narrowed down to either SupportedColor.BLUE or SupportedColor.YELLOW. After the switch statement, the compiler knows that 'supportedColor' has a type of never since both BLUE and YELLOW have been accounted for. If you uncomment the code after the switch block, the compiler will show an error in TS3.7+ stating that it is unreachable.
Thus, all code paths return a value, and the compiler does not raise any complaints.
Now, let's consider what happens when we change the enum values to be calculated instead of literals:
const enum Color {
BLUE = 'Blue',
YELLOW = 'Yellow',
RED = 'Red'
}
// calculated enum
const enum SupportedColor {
BLUE = Color.BLUE,
YELLOW = Color.YELLOW
}
function doSomething(supportedColor: SupportedColor): boolean { // error!
switch (supportedColor) {
case SupportedColor.BLUE:
supportedColor // SupportedColor
return true;
case SupportedColor.YELLOW:
supportedColor // SupportedColor
return true;
}
supportedColor // SupportedColor
}
Here, the enum type SupportedColor is no longer regarded as a union type. Calculated enums differ from union enums. As a result, the compiler cannot narrow down the type of 'supportedColor' in or after the switch statement. The type remains as SupportedColor throughout. Since the switch statement is not exhaustive, the compiler throws an error indicating that the function may not always return a value.
This explains why calculated enums are handled differently, as stated in the documentation regarding union enums versus calculated enums. While the docs don't elaborate on the reasons behind this design, it seems intentional according to the TypeScript team. It is suggested that if you want union enums, stick to using literal values rather than calculated ones.
I hope this clarifies things; best of luck!