-
Notifications
You must be signed in to change notification settings - Fork 3.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[question] TypeScript binding of enums? #18585
Comments
You can do something like this:
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)
;
}
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 |
Is that correct, though? In my project I'm seeing enums come across the language barrier as objects with shape
becomes in JS
|
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, |
Are TypeScript-enums only available via With the build-in 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. 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. |
I ended up writing a python script that parses my C++ headers, and generate definitions for them. 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 It's cool that you found a solution. But I don't get it how to use Can you give a full small example ? For example, with an enum like: enum E{A, B}; Thanks. |
Hello @bansan85 ! Here is a generic converter for any enum type. This registers and converts any enum from/to number. /** @brief Generate a typescript type for a given cpp type */
template<typename Type>
struct GenerateTypescriptType
{
EMSCRIPTEN_DECLARE_VAL_TYPE(TypescriptType);
};
/** @brief Register any enum binding type */
template<typename EnumType>
requires std::is_enum_v<EnumType>
struct BindingType<EnumType> : public BindingType<val>
{
/** @brief Convert enum to wire */
[[nodiscard]] static inline WireType toWireType(const EnumType value, rvp::default_tag tag) noexcept
{
return BindingType<val>::toWireType(
GenerateTypescriptType<EnumType>::TypescriptType(val(std::to_underlying(value))),
tag
);
}
/** @brief Convert wire to enum */
[[nodiscard]] static inline EnumType fromWireType(const WireType wire) noexcept
{
const auto value = BindingType<val>::fromWireType(wire);
AssertRelease(value.isNumber(), "Invalid enum type");
return EnumType(value.as<std::underlying_type_t<EnumType>>());
}
}; I still have to register the enum binding like that : EMSCRIPTEN_BINDINGS(MyLibrary)
{
// GroupCategory
emscripten::register_type<MyEnum>("MyEnum");
} Finally, here is the python script that parse enums of file list and store them into the Typescript binding file. import re
import os
# Enumarate files to parse
scriptsDir = os.path.dirname(os.path.abspath(__file__))
filesToBind = [
scriptsDir + "/../Path/To/CppFile/Enums.hpp",
scriptsDir + "/../Path/To/CppFile/Enums2.hpp",
]
# Output file
outputPath = scriptsDir + "../Wasm/LiveClientTypes.d.ts"
# Parse enums from cpp file
def parseEnumsFromCppFile(filePath):
"""
Parses C++ file for enumerations and stores them in a dictionary.
Args:
filePath (str): The path to the C++ header file.
Returns:
dict: A dictionary with enum names as keys and dictionaries of enum values as values.
"""
enumDict = {}
# enumPattern = re.compile(r"enum\s+class\s+(\w+)\s*(:\s*\w+\s*)?\{([^}]+)\};", re.DOTALL)
enumPattern = re.compile(r"enum\s+class\s+(\w+)\s*(?::\s*[\w:]+\s*)?\{([^}]+)\};", re.DOTALL)
# Open file
with open(filePath, 'r') as file:
# Read file
content = file.read()
# Remove single line comments
content = re.sub(r"//.*", "", content)
# Remove block comments
content = re.sub(r"/\*.*?\*/", "", content)
# Find all enums
enums = enumPattern.findall(content)
# Parse enums
for enum in enums:
enumName, enumValues = enum
value_dict = {}
# Split the enum values into individual items, remove spaces and empty lines
entries = enumValues.split(',')
baseValue = 0 # Default start value
for entry in entries:
entry = entry.strip()
if entry:
if '=' in entry:
key, value = entry.split('=')
key = key.strip()
value = int(value.strip(), 0)
baseValue = value
else:
key = entry.strip()
value = baseValue
value_dict[key] = value
baseValue += 1
enumDict[enumName] = value_dict
return enumDict
# Generated bindings
output = ''
# Generate bindings for every file
for fileToBind in filesToBind:
enums = parseEnumsFromCppFile(fileToBind)
for enum in enums:
output += 'export enum ' + enum + ' {\n'
fields = enums[enum]
for field in fields:
value = fields[field]
output += ' ' + field + ' = ' + str(value) + ',\n'
output += '}\n\n'
# Read the existing content of the file
with open(outputPath, 'r', encoding='utf-8') as file:
originalContent = file.read()
# Write the new text followed by the original content
with open(outputPath, 'w', encoding='utf-8') as file:
file.write(output + originalContent) Hope it helps ! |
Thanks a lot for your fast sharing. Yes, it helped me to understand your solution. But it's not what I expected 😕 . You converted C++ type to Javascript number (Yes, that what you said but I didn't expected that Javascript will lose all other information). So you lose the type and the naming of each value in the enumerator. On my side, I would like to keep the name of the C++ enum in the Javascript object. See below a Firefox log of an class object I will still investigate and see if I use something like |
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,
The text was updated successfully, but these errors were encountered: