- The types exist whether you write them down or not.
- If they're not written down, they're written down in your head.
- Your head is very volatile and hard for others to access.
- Typing is an incredibly good form of documentation.
- JSDoc and TypeScript are standards/formats for typing. Like any tools, they both have advantages and disadvantages. Neither is objectively better than the other.
- Make informed decisions on how you'll describe your types, and then be consistent and unsurprising.
- A type checker is the computer saying, "okay then, prove it" about your program's type validity.
- Not every program benefits from the same amount of "prove it."
- Too much can be as bad as too little. You're wasting resources proving throwaway code.
- I like languages that let you decide how much you need to "prove it."
Modern HTML/CSS with Web Components and JSDoc is underrated. Not for everyone but should be more in the running for a modern frontend stack than it is.
Then Google Closure Compiler came along which added type safety via JSDOC and TS came along with (TS)JSDoc support and it's own TS syntax.
The community chose native TS and Google Closure compiler slipped away into the background.
So (TS)JSDoc support is a relic from when Microsoft was trying to get market share from Google.
Today in 2025, TS offers so much more than the (TS)JSDoc implementation. Generics, Enums, Utility types, Type Testing in Vitest, typeguards, plus other stuff.
Today I use TS. I also use plain JSDoc for documentation. e.g. @link and @see for docs. Or @deprecated when I'm flagging a method to be removed. @example for a quick look up of how to use a component.
TS and plain JSDoc are both important together. But (TS)JSDoc alone, is a relic of the past.
2. you can have navigation that goes to typescript file instead of definition, just arrange your exports in package.json correctly (first ones take precedence)
ok i didn't think about this, that's an underrated benefit
If you define a type in a file with @typedef, it is automatically exported and there is nothing you can do to control that: https://github.com/microsoft/TypeScript/issues/46011
I tried making a library this way and lacking control over the visibility of the exported types was really painful; it made my intellisense awful because every type I defined at the root was exported from the library
Granted they initially weren't down that path, but they course corrected it on time, and not much people use stuff like enums in new code.
I choose to use it because I didn't want to deal with a build step for a smaller project. The project has grown and I am looking at adding a build step for bundling but still not too worried about using JSDoc over TS.
This might be my config, but one thing that does annoy me is whenever I define a lambda, I need to add an doc type. I guess if that's disincentivising me from writing lambdas maybe I should just add a TS compile step lol.
----------------------
Here's an example - I got some config typed with this function https://github.com/AKST/analysis-notebook/blob/c9fea8b465317... - Here's the type https://github.com/AKST/analysis-notebook/blob/c9fea8b465317... - And here's something to generate a more complicated type for defining config knobs https://github.com/AKST/analysis-notebook/blob/c9fea8b465317...
Basically, the fact that it works does not mean it works well, and I don't recommend anyone going in this other direction unless they understand what they are getting into.
More broadly, this is the default behavior of JS even without JSDoc blocks, and it ought to be the default behavior everywhere, including TS. I'm not alone in this sentiment, but it's incredibly contentious. There's been an open GH issue about it with hundreds of replies for years. I have no idea why they can't just pick a different shortcut for viewing types and call it a day. They'd be doing the entire ecosystem a favor.
Best thing for me was not removing the code transformation (convert ts to js), but separate runtime code with documentation code, like the types. Gives you much more clear insight that the types you write to describe what you expect the type will be is not forced on runtime, but is just for you as developer to know it. And when you validate the input is the right type, it is much more clear that it is the runtime type validation.
You can still use JSDoc in your typescript files, but why do you want to do that? There is no reason to do that.
So using JSDoc or Type Annotation, both works the same, same benefits, it is only personal preferences. Both have its pros and cons. For me the JSDoc has more benefits. Some other people prefer annotations. But there is not 1 that is better in controlling the types, has more options. (Also the enum can be done if you are using JSDoc, but is a little different)
Just use TypeScript.
You should occasionally look at the build artifacts of your framework but also ask yourself whether it is worth it to write code that may not represent what actually ends up being executed.
Lately I just use vite with no starter template but with web components and css modules. It at least feels more convenient than using any framework or library.
npm displays packages with bundled TypeScript declarations https://github.blog/changelog/2020-12-16-npm-displays-packag...
JSDoc-typed node modules require special configuration in consumers to be useful https://github.com/microsoft/TypeScript/issues/19145
Then, paradoxically, with no error checking at runtime, it becomes fully possible for JS code to call into TS code in a way that breaks the shit out of the TS compiler's assumptions. In philosophy then TS and JS are as incompatible as GPL and EULA
A bonus of this approach is that it clearly delineates what type information is available at runtime, vs what type information is just a comment. (especially useful when working with serialized data).
I also like that it generally means all of the preconditions and postconditions for functions are in a single place (both the prose and the technical information are in the JSDoc comment), keeping the code itself low-noise and boiled down to its essence, and providing a natural place to write example inputs and outputs to guide usage.
As for generic types, I use them extensively, and this is made less verbose e.g. on function calls by using an @type annotation on the function which accepts a TypeScript signature without needing separate @template + @param annotations.
It's awesome stuff, and I'm happy that TypeScript works so well with JSDoc!
Also, you can use both (if that's what you prefer).
If it's your own library you can add declarationMap: true to your tsconfig.
I almost always want to see the source when I cmd+click.
Once we get it, there is still a solid decade before runtimes support it, and optimistically, still more 10 years minimum having to deal with an interpreted language that has acquired an unecessary build step.
I absolutely hated when PHP switched from a phpDoc culture with static analysis (and IDE inconsistencies that would click-take you to stubs as well) to actual types. Not because I hate types, but because of the transition period. Once it's gone, it's such a relief to get rid of unecessary docblocks.
/
index.js
index.d.ts
Where values in `index.js` can be typed in the header file with complete TypeScript syntax and those types are present in the target file via intellisense and type checking // index.js
function useStr(x){} // has intellisense for "string"
useStr("Hello")
useStr(42) // IDE and type checker error
And // index.d.ts
declare function useStr(x: string): voidDiscriminated unions, conditional types, mapped types, template literal types, recursive type aliases, higher-kinded type patterns, generic constraints with conditional inference, the infer keyword, never type exhaustiveness checking, const assertions, readonly tuple and array inference, exact object types, key remapping in mapped types, indexed access types, variance annotations, assertion signatures, strict null checking with flow-sensitive narrowing, definite assignment assertions, the satisfies operator, declaration merging, module augmentation, symbol-typed properties, type-safe enums
…and not written via comments
However, the precision and completeness is not nearly what can be expressed in TypeScript. With generics particularly.
I vividly remember being in a meeting with the Exchange team (about building shared frontend components) arguing for us to adopt TS instead as it had a better experience and very rapidly growing popularity (that was about 10 years ago). Plus, as strong as Nikhil [0] was, he was basically the only person behind ScriptSharp while TS had a whole team.
Of course, this being MSFT, this effort went no where. While true that the TS toolchain lacked the tree-shaking that ScriptSharp had, I was just annoyed that we had to build stuff using what was obviously an dead-ish language with limited support, many flaws, and no resources to improve it.
But hey, at least it wasn’t GWT.
---
In WebStorm, jsdoc can be rendered in HTML, which makes the code easier to scan. Here's a side-by-side VSCode vs WebStorm:
https://x.com/efortis/status/1989776568676221137
---
And in jsdoc you can have an inline description:
@prop {number} width Video width in pixelshttps://github.com/tc39/proposal-type-annotations?tab=readme...
But it’s more annoying than just writing TypeScript. There are ways to express just about everything TypeScript can but they’re all more difficult and convoluted (generics are a great example). For a project of any reasonable size I’m still going to advocate to use TypeScript.
When other developers and non-developers look at JavaScript developers as small children it’s because the maturity difference is very evident from the outside. Once developers get past basic literacy they are free to worry about architecture, performance, scale, platform independence, and more. For most JavaScript developers they just expect some framework to do it for them.