Using TypeScript JSDoc in combination with Closure Compiler

110 views
Skip to first unread message

Jesper van den Ende

unread,
Oct 28, 2021, 3:06:12 PM10/28/21
to Closure Compiler Discuss
I recently started adding JSDoc comments for IntelliSense to my javascript files. But this starts to very quickly break Closure Compiler build support.

For instance, TypeScript expects:
/** @typedef {{myParam: string}} MyObject */

Whereas Closure Compiler expects:
/** @typedef {{myParam: string}} */
var MyObject;

Does anyone have any advice on how to deal with this?
Should I add an extra pass in my build step that simply strips all jsdoc comments before passing it onto Closure Compiler?
Or is there a tool that easily converts TypeScript compatible code to Closure compatible code? I've heard about Tsickle but afaik that is meant for code written in TypeScript, as opposed to JavaScript with JSDoc.

How much of an impact do JSDoc comments really have on performance? Do they really speed things up significantly or are they mostly meant for generating warnings? 

Would it make sense for Closure Compiler to add more support for TypeScript like syntax? Or is this generally a non-goal for the Closure team?

Bradford Smith

unread,
Nov 4, 2021, 2:17:48 PM11/4/21
to Closure Compiler Discuss
Although both use the name "JSDoc" the documentation comments that closure-compiler recognizes are definitely not the same as the ones defined at http://jsdoc.app, which I presume is the form that IntelliSense is trying to use to give you editor support.
AFAIK the two forms of documentation comments developed completely independently of each other.

Sadly I don't know of any answer that will satisfy here.

If you start using the closure-compiler JSDoc, then closure-compiler's type-based optimizations will be able to better optimize your code,
but the comments will fail to help, and possibly confuse IntelliSense.

OTOH, as you discovered, closure-compiler will choke on the JSDoc comments accepted by IntelliSense.

It sounds like your current goal is to improve your editing experience and you don't really care about improving optimization.
If that's the case, you could probably write a tool that would strip out the JSDoc comments before passing the code to closure-compiler.

If a tool existed that could translate between the IntelliSense friendly and closure-compiler friendly forms that would be nicer, but
I don't know of any such tool and it would be non-trivial to write one. You are correct that tsickle is not designed for this purpose.

Best regards,
Bradford

Jesper van den Ende

unread,
Nov 4, 2021, 3:23:57 PM11/4/21
to Closure Compiler Discuss
Thanks for the info Bradford!

For now I managed to strip all JSDoc using rollup-plugin-cleanup and that got rid of 95% of the warnings.
I'm sure I can get rid of the other warnings either manually or with compiler directives somehow.

I do somewhat care about performance/build size. I'm writing a library that should at the very least be compatible with Closure Compiler but preferably also be as small as possible when imported in a Closure Compiler project.
Do you know by any chance to what extent types actually have an impact on compiled code? I couldn't find much about this other than the compiler being able to inline `@const` variables, but I'm already using const instead of var everywhere anyway.

Bradford Smith

unread,
Nov 5, 2021, 2:34:01 PM11/5/21
to Closure Compiler Discuss
The biggest impact that type information can have on output code size is due to disambiguation.

Most of the compiler's optimization passes completely ignore type information.
However, if disambiguation is enabled then type information will be used to rename logically distinct properties that have the same name so they will be clearly different.

For example, if you have a `Foo` class and a `Bar` class that are not related, and they both have a method named `doSomething()` the disambiguation pass
will rename them to something like `Foo$doSomething` and `Bar$doSomething` so that later passes will know for certain they are different and can optimize them independently.
This could lead to one of them being completely removed as unused or parameters that are unused in one being optimized away without affecting the other.

When this works it can be a really big win for code size.

If I understand you correctly, you're working on a JS library that you expect full applications to use by compiling it in with their own code, maybe with closure-compiler, maybe not.
I'm curious to know how you achieve this level of flexibility. Compilation with closure-compiler almost demands that you use `goog.module` to set up dependencies between
your source files, and once you've done that you have to compile it with closure-compiler to get working code.

If instead you mean you expect to provide your library in an already compiled form and the client applications will side-load it at runtime,
you'll need to make sure closure-compiler doesn't rename the globals and properties you intend to be public by using an externs file.
closure-compiler was designed with the assumption that the result of its compilation is a full application, so it feels free to rename anything that isn't clearly imported from outside of the sources it can see.

Best regards,
Bradford

Jesper van den Ende

unread,
Nov 5, 2021, 5:06:14 PM11/5/21
to Closure Compiler Discuss
Ah I see, interesting. That sounds like types could have a serious impact. I'll leave my setup as is for now but I'll do some experimenting in the future then.
My code contains some reasonably complex typescript within JSDoc, for which I really don't think translating is a viable option.
But I think a tool for some of the more basic types could very well be worth the effort.

I am indeed working on a library that full applications might compile in with their own code. I'm still working out the details but I think it could work.
It's pretty close to the setup three.js is using. Where part of the repository functions as a library that you can use in your application, but another part of the repository contains a web based editor, that itself uses the library to function.
The editor has the capability to build source files for you, so it should generally require very little effort from the user to setup a build pipeline. Only if you really want to go crazy with customisation, you can use the library on its own.
So far it's working pretty well. The editor first makes a build using rollup (in browser) and these built files get sent to Closure Compiler. At the moment I'm using a websocket for this as I couldn't get Closure Compiler to run in the browser without any major porting effort.
The biggest downside of this setup is that the generated code from rollup is not very Closure Compiler friendly. Mainly the @typedefs get removed because rollup thinks they are unused variables. But other than that it seems to compile and run fine so far. I did run across many variables that did not get renamed because they are names that are also used in externs. I wasn't aware this is caused by the lack of types like you said. So I'm hoping the translation tool could take care of this.
I'm not sure on exact differences between `goog.module` and ES6 imports. I have really only used imports so far and haven't even touched `goog.module`. This seems to work ok, even for dynamic imports and even with the rollup pass in between.
Reply all
Reply to author
Forward
0 new messages