emscripten
emscripten copied to clipboard
[question] TypeScript binding of enums?
Hi,
I'm trying to wrap my head around how the enums are exposed with embind. Is there a documentation which provides a basic ".d.ts"-kinda description how is the ""class" structured, what interfaces it has? I'm having hard time to map them properly to ".d.ts", e.g. the mapped values don't behave like "normal" TypeScript enums (e.g. values mapped to numbers of strings). Any idea? Thanks,
You can do something like this:
my_enum.cc
enum OldStyle {
OLD_STYLE_ONE,
OLD_STYLE_TWO
};
enum class NewStyle {
ONE,
TWO
};
EMSCRIPTEN_BINDINGS(my_enum_example) {
enum_<OldStyle>("OldStyle")
.value("ONE", OLD_STYLE_ONE)
.value("TWO", OLD_STYLE_TWO)
;
enum_<NewStyle>("NewStyle")
.value("ONE", NewStyle::ONE)
.value("TWO", NewStyle::TWO)
;
}
my_enum.d.ts
export declare enum OldStyle {
ONE = 0,
TWO = 1,
}
export declare enum NewStyle {
ONE = 0,
TWO = 1,
}
export interface MyModule extends EmscriptenModule {
OldStyle: typeof OldStyle
NewStyle: typeof NewStyle
}
I learned this by examining the output of tsembind. EmscriptenModule comes from @types/emscripten. For free functions, classes, class methods and static class methods, see this comment and this comment.
Is that correct, though?
In my project I'm seeing enums come across the language barrier as objects with shape { value: number, constructor: Function }
enum<OldStyle>("OldStyle")
.value("ONE", OLD_STYLE_ONE);
becomes in JS
Module.OldStyle.ONE // { value: 0, constructor: f }
I came here for the same reason. Enums are not represented as simple constants, but objects, so we cannot use them directly as values. However, switch statements work as expected. Yet, the documentation is not correct here!
Are TypeScript-enums only available via tsembind?
With the build-in --embind-emit-tsd generator I'm getting non-enum .d.ts definitions like this:
export interface MyEnumValue<T extends number> {
value: T;
}
export type MyEnum = MyEnumValue<0>|MyEnumValue<1>|MyEnumValue<2> ....
interface EmbindModule {
MyEnum: {Value1: MyEnumValue<0>, Value2: MyEnumValue<1>, ...
...
}
instead of
export declare enum MyEnum {
Value1 = 0,
Value2 = 1,
...
}
The generated TS bindings for enums better matches how embind represents enums in JS. There's been talk of changing embind's enums into TS enums, but I haven't looked much into what the effects would be.
More discussion in #19387.
This non-constant representation is very annoying and there is currently no work around to generate typescript enums. Is there any plan on developing such a feature or should I try to modify tooling code myself ?
Thank you
I'd like to look into at some point, but it's not on my immediate plans. I'd be happy to review a PR if you're interested in looking into it though.
@brendandahl
I ended up writing a python script that parses my C++ headers, and generate definitions for them.
Using EMSCRIPTEN_DECLARE_VAL_TYPE to generate a type, emscripten::register_type to register a generated enum and finally emscripten::internal::BindingType to register all C++ enums as numbers.
This is a bit hacky but this allows me to not register enumeration values one by one and potentially forget to update it when I change them.