Related: #9674 #7083
I would like to discuss the current best way to use typing features of TypeScript with Emscripten. If you are looking for a WebIDL -> TypeScript .d.ts converter for C++ application specifically, you may refer to the above two issues.
I recently found @types/emscripten from NPM and I spent a few hours to figure out how to integrate it with my Emscripten project and contributed the package a little bit too. The .d.ts typing file can be found here: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/emscripten/index.d.ts
Suppose I make add.c like this:
// add.c
int add (int a, int b) {
return a + b;
}
Two ways I have figured out:
add--post-js.tsadd.d.ts + add--post-js.jsIn my case I found option 2 is better. Will be explained later.
add--post-js.ts/// <reference types="emscripten" />
/** Above will import declarations from @types/emscripten, including Module etc. */
// This will merge to the existing EmscriptenModule interface from @types/emscripten
// If this doesn't work, try globalThis.EmscriptenModule instead.
interface EmscriptenModule {
// Module.cwrap() will be available by doing this.
// Requires -s "EXTRA_EXPORTED_RUNTIME_METHODS=['cwrap']"
cwrap: typeof cwrap;
// Exported from add.cpp
// Requires "EXPORTED_FUNCTIONS=['_add']"
_add(number, number): number;
// or using cwrap. See below
add(number, number): number;
}
Module['onRuntimeInitialized'] = function() {
// Just Module._add() will work, but I'm just demonstrating usage of cwrap()
Module['add'] = cwrap('add', 'number', ['number', 'number']);
}
add.d.ts + add--post-js.js/// <reference types="emscripten" />
/** Above will import declarations from @types/emscripten, including Module etc. */
// This will merge to the existing EmscriptenModule interface from @types/emscripten
// If this doesn't work, try globalThis.EmscriptenModule instead.
export interface AddModule extends EmscriptenModule {
// Module.cwrap() will be available by doing this.
// Requires -s "EXTRA_EXPORTED_RUNTIME_METHODS=['cwrap']"
cwrap: typeof cwrap;
// Exported from add.cpp
// Requires "EXPORTED_FUNCTIONS=['_add']"
_add(number, number): number;
// or using cwrap. See below
add(number, number): number;
}
// Declare any name
declare const addModule: AddModule;
// Only for -s MODULARIZE=1
export = addModule;
// Only for -s MODULARIZE=1 -s EXPORT_ES6=1
export default addModule;
/// <reference types="emscripten" />
/** Above will import declarations from @types/emscripten, including Module etc. */
/** It is not .ts file but declaring reference will pass TypeScript Check. */
Module['onRuntimeInitialized'] = function() {
// Just Module._add() will work, but I'm just demontrating usage of cwrap
Module['add'] = cwrap('add', 'number', ['number', 'number']);
}
I know, both of them don't particularly look pretty but they are the cleanest ones so far.
I found that the second one is better for two reasons: add--post-js.ts file must be compiled to js in order to work with emcc command. And there are problems when you want to use export statement because -s MODULARIZE=1 creates another export statement automatically.
Please share any better tricks if you have one. Also you will notice that @types/emscripten is not complete when you look at the source code it would be great if anyone can make the package better.
Thanks :)
cc @jgravelle-google (maybe this could benefit from interface types?)
If I understand correctly, this is about using C++ functions in a TypeScript application? And ideally having TS types for the C++ as well.
Probably the most ergonomic tool would be using embind's EMSCRIPTEN_BINDINGS mechanism (https://emscripten.org/docs/porting/connecting_cpp_and_javascript/embind.html#a-quick-example). Interface types should help here in the future, by giving us a custom section a TS tool could autogenerate the .d.ts from.
@jgravelle-google Right. This is the current available way for someone who want to use C/C++ functions in a TS application right now. I am not asking Emscripten devs to do something. I posted this because I cannot find any guides on how to achieve this on the internet, and I thought posting it as an issue here is better than writing it in my blog because someone might have a better idea.
As you mention, In the future we could automatically generate .d.ts using embind or the WebIDL binder. I believe #9674 and #7083 already discuss about it.
I am not asking Emscripten devs to do something.
Oh, I assumed you weren't yeah. Just thinking out loud "if I were to do this what would I do," in case that counts as having a better idea. And also thinking about how Interface Types would apply here specifically.
WebIDL binder is probably closer to what TS would expect. Is there already a general-purpose .d.ts generator from webidl for web APIs? Though I imagine there's still some bridging needed to make the formats line up.
Is there already a general-purpose .d.ts generator from webidl for web APIs?
I think microsoft/TSJS-lib-generator is the best bet. The TypeScript Team do not hand-write Web API type definitions from scratch but generate .d.ts files from various WebIDL sources (Source: https://github.com/microsoft/TypeScript/issues/3027). I haven't done any experiments with this but I guess we need some work to adapt it to Emscripten.
@jgravelle-google With protobuj.js supporting typescript, and the fact we're moving to a place where we'll want to interop across multiple languages (typescript, mono/c#, golang, rust, kotlin, c++, etc.) within the browser/webassembly, would it make more sense to focus on good protobuf support for emscripten? Then we can start using "event bus" patterns within the browser/webassembly to interop across different webassembly languages.
I have written a tool that can generate type definitions for emscripten modules.
For now i have tested it only with the ammo.js project
The tool itself is based on the https://github.com/microsoft/TSJS-lib-generator. Unfortunately TSJS-lib-generator itself can not be used for emscripten as it is very specific in some parts. I borrowed some code and concepts to make it work for ammo and hopefully for other emscripten projects as well. Just needs some more testing to discover edge cases.
Very interesting @giniedp !
If you think it's ready, a PR to add a link to that in the docs would be great.
Most helpful comment
I have written a tool that can generate type definitions for emscripten modules.
For now i have tested it only with the ammo.js project
The tool itself is based on the https://github.com/microsoft/TSJS-lib-generator. Unfortunately TSJS-lib-generator itself can not be used for emscripten as it is very specific in some parts. I borrowed some code and concepts to make it work for ammo and hopefully for other emscripten projects as well. Just needs some more testing to discover edge cases.