dop251/goja

Question: is TypeScript support on the roadmap?

eric-s-raymond opened this issue · 3 comments

goja sounds ideal for a proj4ct I have in mind, but I'd really likee to have Typescript support rather than plain ESCMAscript. Is this on the roadmap?

dop251 commented

If you mean working with Typescript directly without transpiling, I don't have such plans. Otherwise you can run tsc either with goja or with an another ECMAScript engine and run the resulting code in goja, just like you would do with nodejs, including support for sourcemaps.

FWIW I've been having great success with https://github.com/clarkmcc/go-typescript, which essentially just runs tsc inside goja.
It took some work to find the best tsc compiler options such that the emitted code can take the most advantage of goja's feature support without trying to make use of unsupported features.

For now I'm setting the target to es2017 and setting module to commonjs such that if you use the modern import/ export syntax in your TypeScript code, it still emits the more compatible require() syntax - which will only work if you're using the Registry from goja_nodejs, or you could roll your own module loader (go-typescript seems to have their own for AMD modules, which I haven't really touched).
Using es2017 as the target means that tsc will leave syntax like for (const entry of array) and async/await untouched, which is preferable since goja supports these, so you get cleaner and simpler output due to tsc not having to include polyfills[1].

If using the Registry from goja_nodejs, you can set a custom source loader to be used, that will take care of transparently transpiling any TypeScript whenever the scripts attempt to load a module written in TS. (I have an example here but it's probably overly complex and too specific to my project).
If dealing with native modules (of the style present in goja_nodejs) I also recommend setting the esModuleInterop tsc compiler option to true, to work around problems related to default imports when using certain styles of import in TS code. (Do note that this is likely off-standard, and can probably be worked around by making the native modules define a default field in their exports object).

Through all this I've been able to basically make TypeScript a first-class citizen in my project (even for browser usage, the server will take care of transpiling TypeScript before serving it to the browser!) but of course there are still some teething issues, such as line numbers in goja stack traces not matching those of the original TS code. I think goja has some support for source maps, so maybe I'll try to get that sorted out.

[1] Actually, I even ran into problems with the polyfill code emitted by tsc for that iteration syntax, when iterators - such as those returned by Map.prototype.values() - were involved. The emitted code was attempting to access the length of an iterator, which isn't a thing. You can use the tsc option downlevelIteration set to false to work around this problem specifically.

It turns out that solving the source mapping problem was as simple as setting the tsc compiler option inlineSourceMap to true. goja can parse just fine the base64 source maps that tsc will include in the result.