String Enum values compile to the key rather than source value
Describe the bug
In typescript, we can represent string enums like so:
export enum EnvironmentName {
DEV = 'dev',
QA = 'qa',
FOO = 'bar'
}
However, in all languages besides js, jsii compiles these so that the key name is used. For instance, python output looks like:
@jsii.enum(jsii_type="@example/my-package.EnvironmentName")
class EnvironmentName(enum.Enum):
DEV = "DEV"
QA = "QA"
FOO = "FOO"
When using the enum as a type, this doesn't matter, as the literal value is unimportant. But when outputting a string, Python developers are now outputting FOO whereas TS/JS users of the same construct output bar. This breaks interoperability.
Expected Behavior
The string representation of an enum value in all languages should match the source code. As noted in the jsii design tenants:
- jsii applications behave identically regardless of the language they are written in. It favors correctness over performance.
- Unsupported idioms will cause a compile-time error to be emitted.
- When prohibiting an idiom, jsii strives to provide an error message that gives the user insight into why the pattern cannot be supported.
Applications should behave identically, and if that is not possible, there should be a compiler error for string enums where the key and value do not match.
Current Behavior
An application which uses runtime validation of enum values will fail in languages other than JS. For instance:
export const validateEnv = (env: EnvironmentName) => {
if (Object.values(EnvironmentName).includes(env)) {
throw new TypeError(`Value "${env}" is not a valid environment name.`);
}
}
I believe this is because the JS assembly is used when calling Object.values(), and the value passed in doesn't match JS values due to the serialization differences.
Reproduction Steps
- Create a construct library which uses a string enum's values inside functional code.
- Publish package to NPM and another supported target.
- Import the package in the target language and call the function.
- Observe output.
Possible Solution
Rework how JSII models string enum values. Not sure how complicated this is, but it seems important for correctness.
Additional Information/Context
No response
SDK version used
jsii=5.2.8; jsii-pacmak=1.89.0
Environment details (OS name and version, etc.)
MacOS 11.7.10
Not entirely sure about the implications here. We might need to enforce that always enum keys == value
+1 on this - still seeing this behavior in 2025.
+1 on this - still seeing this behavior in 2025.
To add context, Derrike and I are working on this same thing using JSII to build a CDK construct library and we were concerned the implication here is that the underlying CFN generated is incorrect.
Looks like it might not be after doing some deeper research on an aws-cdk enum that is afflicted by this problem.
Here's a CDK example - the WebSocketAuthroizerType for an API Gateway Web Socket API. This enum has differing keys and values and the value is what is important since it goes into the CloudFormation template.
This enum is used as a prop type to the WebSocketAuthorizerProps which then is used directly to synthesize CloudFormation here.
After running a quick test using the Python variant of the AWS CDK compiled with JSII seen here:
We can see that the correct value is placed in the CloudFormation template here:
This is despite the Python typing for the enum being affected by this issue seen here:
So at surface it seems like this might not be a major issue. The language bindings will show enums with keys that match their strings but since the bindings are just fronts for using the actual TypeScript module under the hood, the correct source values still get used where they're needed.
However, it is still confusing and unexpected behavior for authors using the JSII to build libraries and could still be an issue if one expects a consumer of the library to be able to use the source values reflectively in another language.