TypeScript vs. jsDoc

Can jsDoc provide a similar editor experience while ditching the build step?

TypeScript vs. jsDoc

In a recent Javascript project I was part of, the team was given a lot of freedom regarding the choice of tech stack. We knew we didn't want Node/NPM because our app would be part of a suite of apps in a high-security environment, and there had been a recent flare-up of security incidents with NPM packages.

So we actively researched alternative JS runtimes for testing and running scripts. Bun wasn‘t ready then, so we quickly settled on Deno. Deno has a great out-of-the-box experience, being everything from a runtime, linter, formatter, all in a single binary.

One notable feature is the TypeScript support, making TypeScript a first-class option. So we took a close look at TypeScript, and I was especially delighted, as I had some experience with C# and its static typing and prefer it over dynamic typing.

Around the same time, I learned that TypeScript is actually designed by the same guy who also made C#, Anders Hejlsberg, who also happened to design Turbo Pascal, the first programming language I learned in school. It's a bit crazy how full circle this feels.

But the team lead after a couple of weeks decided against TypeScript, much to my dismay. So we compiled everything down to Javascript, made some manual modifications and moved on. But after having had a taste of static typing in the Javascript context and witnessing the power of Intellisense in my IDE, I just couldn't let go of it. What had originally been a tidy workspace felt like it was quickly deteriorating into spaghetti code.

So I did some more research on alternatives for static typing in Javascript and discovered jsDoc and Flow. I found jsDoc to have better tool-integration than Flow, so I went that route.

In its simplest form, decorating an ES class with TypeScript looks something like this.

/**
 * Foo class
 * @class
 * @extends Bar
 * @description Does things
 */
class Foo extends Bar {
  
  /** @type number */
  amount;

  /**
   * @constructor
   * @param {number} amount 
   */
  constructor(amount) {
    this.amount = amount;
    this.doThing(amount); // <- throws warning for type mismatch
  }

  /**
   * @method
   * @param {string} name
   * @description Does things
   */
  doThing(name) {
    console.log("Doing things " + name);
  }
  
}

As you can see in the following screenshot, in an IDE like Webstorm, this gives us some nice visual type hints and a warning regarding type mismatch.

In Visual Studio Code you have to add //@ts-check to the top of the file to enable type-checking (which uses TypeScript under the hood). It doesn't give you the visual hints Webstorm does, but it also recognises the type mismatch and gives you some Intellisense.

I think with these simple annotations, jsDoc can give you 90% of the editor user experience that TypeScript gives you, without requiring a build step.

After dropping TypeScript in the aforementioned project, I became quite happy with this solution, as it doesn't require a build step or any special tooling.

Discussion