DEV Community

Cover image for A JSDoc in TypeScript's Clothing
Adam Nathaniel Davis
Adam Nathaniel Davis

Posted on

A JSDoc in TypeScript's Clothing

My recent journey into the Land of the TypeScripts has led me to some odd realizations about what it does - and what it doesn't do. Specifically, I've become aware of some stunning similarities between the "language" of TypeScript and the linting tool we know as JSDoc.

Ground Rules

Throughout this article I'm going to use a handful of key assumptions. I freely admit that these assumptions may not always apply to your projects. And given the way that your team codes, you might feel that there's little in common between TS and JSDoc. But the following "ground rules" are what lead me to question the utility of one over the other:

  1. Given that JSDoc and TS both compile down to plain ol' JavaScript, and given the fact that JS is widely acknowledged as a Functional Programming (FP) language, the "best", "cleanest" code is usually assumed to be that which is broken into its smallest constituent parts. Specifically, "good" JavaScript code is usually identified as having small, concise functions. If you've got a bunch of functions that stretch into hundreds of LoC, there's a good chance that other devs will look at them and crinkle their noses. As the axiom goes: A function does one thing, and does it well.

  2. If we are keeping our functions concise, the biggest obstacle to writing bug-free code is ensuring clean inputs to our functions. Similarly, we strive to ensure a consistent output from our functions. With both TS and JSDoc, it's at least possible to annotate the specific type of every variable.
    But in my experience, this is rare. If we have a LoC like this: const foo = 'bar'; - we rarely bother to specifically annotate that foo is of type string. It's obvious to any first-year dev that foo is of type string. And there's rarely much utility in codifying this for the compiler.

  3. Because functions (and their associated code files) can often be organized in such a way that they're nowhere near the place where they're being invoked, it can be easy to accidentally pass the "wrong" type of data. "Bad" data often leads to "bad" results. So even in TS-heavy (or JSDoc-heavy) codebases, most of the type hinting we're trying to do tends to be focused on function signatures.

Alt Text

The Unmasking

I'll be the first to admit that I've never been a huge fan of JSDoc. Why? Well, every JSDoc annotation is a comment. And, for some time now, I've generally tried to avoid almost all comments in my code. I'm a firm believer that, rather than writing comments that explain your code, it's far better to write self-explanatory code. (That's a subject for a whole other article...)

When I first started doing TS, I had no such reservations. In fact, I was excited about TS's possibilities. After all, I could now start writing some strongly-typed JavaScript code. The possibilities felt immense. But once I started cranking out those sweet, sweet types, some harsh realities began smacking me upside the head:

  1. Still just... JavaScript
    For all the glorious talk about TS's compiler, that compiler is really more of an interpreter. It's just doing a sanity check on your code, and then transpiling it down into plain ol' JavaScript. JSDoc isn't exactly the same. It doesn't afford you any extra "compiler" step. But when you deploy your JSDoc-heavy code, it's still getting run as plain ol' JavaScript.

  2. Worthless for runtime errors
    Although the path to plain ol' JS isn't entirely identical for TS vs. JSDoc, they both share one critical trait (or, if you will, fault): They're both powerless to address RUNTIME errors. In fact, in some respects, I actually prefer the JSDoc approach because every junior dev understands that comments serve no functional purpose. And every JSDoc annotation is a comment. Conversely, it's easy, after writing many hours of TS code, to nearly forget that all that glorious TS magic doesn't even exist at the point that your application is, you know... running.

  3. "Strictly"... malleable
    TS & JSDoc are both as loose - or as strict - as you'd like them to be. Granted, with TS it's easy to set global configs in such a way that it's more... conspicuous to bypass the compiler. But at the end of the day, neither one is doing a single thing to change the way that JS operates. They're only changing the way that you (and the other members of your team) grok the code as you're writing it.

  4. Verbosity
    One of the things that always turned me off about JSDoc was the gaggle of extra LoC that littered every one of my files. Sure, those lines of "code" didn't contain any logic. They were all... comments. But I hated taking a nice, clean, concise function that accepts, say, four inputs and returns one output, and chunking seven extra lines of comments above the function just to explain what those inputs/outputs should be. Then I started writing TS - and I find that I'm routinely writing many extra LoC, and many extra functions/types/interfaces, and many extra code files, just to achieve what I was previously doing with vanilla JS. Suddenly, the extra "load" of JSDoc's comments don't seem like such a burden.

  5. Limited to JavaScript's type model
    It's pretty difficult to construct a "serious" type model when you're confined to the types defined in the underlying language. With TS or JSDoc, I can annotate that the firstName argument is type string. Umm... great? I mean, sure, it's nice to signify that firstName can't be, say, a Boolean or an Array. But what if someone passes an empty string for firstName? Is that a "valid" input? Probably not. It's still, technically, a string. But neither TS nor JSDoc are adequately equipped to handle these common "gotchas". To guard against this, we still have to fall back on runtime checks, typically performed at the top of the function.

  6. Less coding tools than IDE tools
    If you don't believe this, try coding up some tasty TS code... in Notepad++, or Sublime, or any other "basic" text editor. Then tell me how fun that experience is. Granted, advanced IDEs are the tools in which nearly all modern code is written. So I'm not painting this as a "knock" against against either TS or JSDoc. But most of the "magic" that we get from TS occurs when our IDE puts one of those warning squigglies under our code. And you can receive the exact same warnings... with JSDoc.

  7. Conspicuous imperfections
    Whenever you're using any tool designed to better annotate your code, it can be insanely annoying when the IDE/compiler somehow gets it "wrong". I gave an example of this in my previous article talking about TS object handling. But the fact is that I've run into the same problem before with JSDoc. I go to great lengths to fastidiously "define" all of my data types. And then, at some random place in the function, I get another of those squiggly underlines. Sometimes this can happen when I know that the code will run perfectly and even after I've gone to great pains to explain to the IDE/compiler how everything should be handled. But sometimes these tools still don't "get it". This is a big reason why I rarely use JSDoc unless the project already uses it as a standard. And it's why I'm already growing somewhat exasperated with TS.

Alt Text

Trade-Offs

To be clear, I'm not trying to claim that TS is equivalent to JSDoc. I'm sure that a few TS fanboys have already fired off some defensive comments below - before they even got to this point in the article.

When I started this little TS journey about a month ago, I dove in with the (misguided) belief that TS and JSDoc had nothing in common. In fact, before I started writing TS, I had never even attempted to compare both of them in the same paradigm.

But the more TS code I write, the more I find myself thinking that TS is "basically" JSDoc, except it's even more verbose, it gets more bogged down in edge-cases (partials, generics, etc), and its "benefits" are even harder to suss out when weighed against the extra overhead of time and cognitive load.

If this sounds like a long rant of "TypeScript bashing", it's not. As far as I can tell (at the time being), the following are the biggest points in TS's favor:

  1. It's easier to set global rules in TS that will more easily be enforced across your entire team and throughout your entire codebase.

  2. Assuming that global rules are set, it's easier to spot when any dev is trying to subvert those rules. An any has a tendency to stand out like a sore thumb during a pull request. Conversely, the way to "subvert" JSDoc rules is to simply... avoid writing the rules altogether. And it's not nearly such a red flag when some dev has simply failed to write something than it is to catch when he's littered all his code with any.

  3. Obviously, there are more "edge cases" that have been accounted for in TS. Some complex, abstract TS types can be an absolute eyesore to mentally parse. But at least you can annotate them. In JSDoc, it can feel borderline impossible to thoroughly annotate those same use cases.

  4. My anecdotal assessment is that TS is a growing, thriving, evolving community. Although I could argue that TS is as much of a "language" as JSDoc, I'll freely admit that JSDoc is fairly... stagnant. I can't really say that it's "growing" in any meaningful way. I suppose its skeptics could even claim that it's dying - although it certainly won't be truly going away any time soon.

  5. There are many devs out there who are passionate about working on TS projects. But even for longtime, hardcore "JS guys", I don't know if there are too many of them getting all giddy just because their next project will be using JSDoc. This is not an inconsequential observation. I fully understand that sometimes, if you're building and maintaining a large team of skilled developers, it can be important to utilize the "hot" technologies - even if the "old, stodgy" tech does nearly the same thing and offers nearly the same benefits.

Alt Text

Conclusion

So what say you, TS fanboys? Have I written enough to royally piss you off? Are you circling your legions of TS acolytes to come burn my house down??

Seriously, though... I don't really care too deeply about TS versus JSDoc. I'm not trying to trash TS. I'm not trying to sing JSDoc's praises. (As I tried to make clear in multiple places above, I actually don't care much for JSDoc either.) This is not some creed imploring everyone to dump their TS codebases and convert them all to JS + JSDoc.

If you already have a huge TS codebase, of course you're gonna stick with TS. If your team is already littered with hardcore TS fans, of course you're gonna stick with TS. In fact, even if you're starting from scratch on a "green fields" project, you might look at the strengths/weaknesses of both and decide that TS is the path for you. And that's fine.

I just sometimes get a kick out of observing the way that Technology X is held up as "The New Hotness". And then, when I get in and actually start playing around with that technology, I find myself thinking, "Hmm, this New Hotness feels an awful lot like... the Old Hotness."

Top comments (14)

Collapse
 
jwp profile image
John Peters • Edited

Mr. Davis,

Nice comparisons here. I too realized after using Typescript for a while that it is nothing more than a metadata generator allowing IDEs to do nice things with the metadata. All the metadata being stripped off at compile time shows us plain ole JavaScript.

But even JavaScript on its own can't detect a null parm being sent in and won't without AOP. The traditional place to validate for last 50 years is in entry to the function. Shoot, Marshall Dillon did it.

But even AOP once thought impossible may have new life with the new decorator standards appearing. A recent article here showed how that works. Which is cool but will present debugging challenges to those unaware.

JavaScript is OK but nothing super over the top. I've never told my own Mother about it. Its major attribute is it's ubiquitous adoption. As it matures; we are seeing Typescript like adaptions, like the class, arrow functions, constructors and now decorators.

The JavaScript of today is nothing like it was and that's a good sign of a thriving language.

Maybe soon it will overtake Typescript's innovation like Rust overtook C++. The only problem is MSFT knows languages extremely well. I don't see them letting up on Typescript innovation. To do so, would leave them nowhere close to JavaScript. They have no language entry in the JavaScript world without Typescript.

Hey, is that your shed on fire over there in your background?

Collapse
 
miketalbot profile image
Mike Talbot ⭐

At the beginning TS was a way of being able to code with fancy new features for sure. Then JS got better as Adam has pointed out in other articles. I guess I've been burned by following the Microsoft "hype" - we built a lot of stuff in XAML and SilverLight and were left hanging. I find myself distrusting anything that is powered by an advertising machine and that is significantly perpetuated by it.

AOP would be fantastic though. I'd dearly love to be using metadata in frameworks.

Collapse
 
bytebodger profile image
Adam Nathaniel Davis • Edited

Great points!

I've been quite intrigued by decorators. The only thing holding them back, at the moment, is the fact that they're still "experimental". And as you point out, even when they're adopted, they also present some new challenges. Nevertheless, I think it's my C# experience that makes me so fond of them. IMHO, they're both incredibly useful and they create rather "clean" code (as opposed to, say, doing it manually by literally wrapping a function/method in another function/method).

I'm also encouraged by the rapid pace of JS's development. In fact, I wrote a whole article about that earlier (dev.to/bytebodger/why-javascript-i...).

I imagine that, as time goes on, TS will continue to evolve. But I also think that some of TS's features will also end up being absorbed into JS. I'm not implying that they'll eventually become the same thing. But jQuery is, IMHO, a useful example.

Nowadays, there's much about the jQuery model of querying DOM elements that you can do without ever loading up jQuery. I can imagine a similar path for TS features in core JS.

Thanks again for the feedback. I'm gonna go check on that shed now...

Collapse
 
jwp profile image
John Peters

Yes, just think of JavaScript adopting type annotations. That could kill Typescript. Who knows maybe Typescript will introduce LINQ.

Thread Thread
 
miketalbot profile image
Mike Talbot ⭐

When I first came to JS from C# I used (what is now) github.com/mihaifm/linq . In the end I felt that map/filter/reduce was enough and slowly it dropped out of my daily practices.

Thread Thread
 
jwp profile image
John Peters

me too, I just loved linq so much, it was the best thing since chewing gum!

Collapse
 
jwp profile image
John Peters

I love your articles and great sense of humor! Have a good day my friend.

Collapse
 
miketalbot profile image
Mike Talbot ⭐

See in Webstorm/IntelliJ JSDoc is just autofolded away for me. It's just a single line above the function. Plus I'd only ever bother on library stuff that is going to be called by someone else at some point, internal functions etc just don't need it.

Collapse
 
bytebodger profile image
Adam Nathaniel Davis

Ahhh, that's a good point. I don't have my WebStorm settings configured to fold those comment blocks. But that would really relieve a lotta scrolling in those places where JSDoc is being used.

But yeah, on internal stuff, it's really not necessary at all.

Collapse
 
thejaredwilcurt profile image
The Jared Wilcurt • Edited

A while back I had an idea (mostly inspired by Vue's prop type approach) of a runtime version of type checking. Here is a quick sketch of it:

const typeError = function (err) {
  throw err;
};

const hString = new Interface({
  type: String,
  required: false,
  default: 'Hello',
  validation: function (value) {
    return value.startsWith('H');
  }
});

function greetTarget (greeting, target) {
  Interface.validate(greeting, hString, typeError);
  try {
    Interface.validate(target, {
      type: String,
      required: false,
      default: 'World'
    });
  } catch (err) {
    console.log('The value supplied for target does not match interface. Falling back to default: ', err.default);
  }

  return greeting + ' ' + target;
}

greetTarget('Hey', 'buddy');
Enter fullscreen mode Exit fullscreen mode

Pros:

  1. Allows for runtime checking
  2. Allows for defaulting values
  3. Allows for custom validation
  4. Allows for multiple types via type: [Number, String] or type: [Array, Set]
  5. Easily extensible. For example you could add a strip: true to the object to tell WebPack or whatever to strip out all type checks of that Interface. Or to strip out all by default unless they have a keep: true. Meaning you can define compile-time-only and run-time specific uses to reduce bloat of your builds.
  6. Can take a defined interface or an object. So object definitions could be reused and ... rest/destructured
  7. Easily reusable. You could have an entire file of validators like export const H_STRING =. Interface validation libraries could be pulled in and used and easily tree-shaken
  8. Can be applied to arguments in a function, or just any one-off variable
  9. Because this is just plain JS it is compatible with everything (JSDoc, TS, React, Vue, Node, IE, whatever).
  10. Linting could be used to enforce usage (like with JSDocs linting), or to require either required or default be set (like in Vue's proptypes linting)
  11. The functions used in the validation could be unit tested easily with tools like Jest.
  12. Could be adopted by TC39 for ES20XX without ANY NEW SYNTAX CHANGED, and easily polyfilled to any previous browser or Node version.
  13. Even if built in to the browser, it would be easily modified or tweaked due to JS's prototypal nature, to add unique features for specific use cases.
  14. Codemods could be used to convert existing TS or JSDoc projects over to this approach

My sketch is rough, but you get the idea. If you want to take a stab at creating a proof of concept let me know and I'll be a beta tester. The function names used can be completely different too. It's not a blueprint, just an idea.

Collapse
 
sergioness profile image
Serhii Krasnovidov

you may want to give gitlab.com/dejawu/ectype a go

Collapse
 
merri profile image
Vesa Piittinen

I'm waiting for TypeCSS :)

Collapse
 
crazy4groovy profile image
crazy4groovy

What think you of this idea?

css-tricks.com/typescript-minus-ty...

I think I like it!

Collapse
 
bytebodger profile image
Adam Nathaniel Davis

I'm down with it. My only concern is that, if I understand the article properly, this is a VSCode-specific solution??

I'm not a big fan of any approach that requires/assumes that everyone will be using the same IDE.