emscripten icon indicating copy to clipboard operation
emscripten copied to clipboard

[question] TypeScript binding of enums?

Open harsszegi opened this issue 2 years ago • 8 comments

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,

harsszegi avatar Jan 24 '23 08:01 harsszegi

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.

harichards avatar Mar 30 '23 23:03 harichards

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 }

kirkwaiblinger avatar Apr 21 '23 23:04 kirkwaiblinger

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!

mike-lischke avatar Jun 19 '23 11:06 mike-lischke

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,
  ...
}

pwuertz avatar Apr 15 '24 09:04 pwuertz

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.

brendandahl avatar Apr 15 '24 21:04 brendandahl

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

MatthieuMv avatar Apr 22 '24 17:04 MatthieuMv

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 avatar Apr 22 '24 20:04 brendandahl

@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.

MatthieuMv avatar Apr 26 '24 09:04 MatthieuMv