/
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): voidThe advantage of JSDoc is that the TypeScript compiler treats it as equivalent to TypeScript source code, which means you've still got access to type checking inside your functions.
One thing I've seen in a few places is still to have DTS files that declare all sorts of types that are used in the application, and then have those files imported by JSDoc comments. That way, you've got TypeScript syntax for your types, which tend to get bulky and difficult to read in JSDoc syntax, but you still don't need a build system because the DTS imports are ignored completely at runtime.
There are three issues with that.
The first is that JSDoc doesn't support everything you need in TypeScript and there is a lot of inlining (like typedef causes collisions, there's no importing a type without also re-exporting it from the importing file unless you inline the import)
The second is that JSDoc isn't picked up from imported libraries (or at least I don't think it is?)
Lastly, you still need a transpiler to remove the comments and build d.ts files.
In the end, JSDoc isn't practical for projects. The header file strategy means you don't need to transpile ever (only a typechecker) and you'd get the full suite of TypeScript functionality - at the cost of synchronization overhead.
For a project using JSDoc, you'll graduate to TypeScript when there is sufficient complexity anyway. Migrating from a d.ts file is way easier (potentially automatable) than migrating from JSDoc.
So even if TypeScript did check the header file against the implementation, you'd still need additional annotations inside the implementation file. At that point, why not put all the annotations inside the implementation file directly?
Regarding your points:
1. JSDoc does support everything that TypeScript supports (that's the point of this article), although it does not necessarily support it as cleanly, hence why projects like Svelte typically use DTS files to define the project's types, and have those be imported inside JSDoc annotations. It's not perfect, but it gets you a long way.
2. You're right, JSDoc isn't picked up from imported libraries, but if you're publishing a library, you'll have a build script there, and at that point it's typical to generate the DTS files and pack those with the source code. That seems fairly reasonable to me — while developing, you don't need to worry about building files, but then when packaging and releasing you do.
3. You don't need a transpiler with JSDoc, the point behind JSDoc is that it's literally just the pre-existing JS documentation syntax. You can (and should) leave the comments in. Even if you're using TypeScript syntax, you should have JSDoc annotations (although adding the types to those annotations is redundant in that case). You can also just include the DTS files. As long as they're only imported from JSDoc, the runtime won't see them.
Personally, having tried out JSDoc-based TypeScript, I'd rather just use the native type stripping in Node for development, and keep with TS syntax, but there are large projects that have made the opposite choice, most noticeably Svelte. So I don't think it's a given that JSDoc users will inevitably graduate to TypeScript (especially when projects have gone in the opposite direction).
Discriminated 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
I really mean it. You can even use the @satisfies operator like so
/** @satisfies {Type} */
Discriminated unions, conditional types, mapped types, template literal types, etc work exactly the same way if you define them in a JSDoc comment as if you define them in a .ts fileHowever, the precision and completeness is not nearly what can be expressed in TypeScript. With generics particularly.
The clunkiest part is in the way you "pass in" a generic to a slot. But this is solved by typing the return type.
I use generics pretty extensively and I've not yet come across a use-case JSDoc couldn't handle
Which is a lot different than vanilla JSDoc [1].
I understand.
Almost any modern IDE is also parsing JSDoc comments through the TypeScript language service. So many people don't realize they are already using TypeScript (hence the name of this post).
I don't think it's particularly controversial to say that the form of JSDoc that the majority of developers are familiar with IS JSDoc. The distinction is more of a historical one than a technical one (since there is no formal JSDoc spec)
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 pixelsThis entirely depends on your tsconfig setup. You can run a JSDoc-typed project in strict mode exactly the same way you would a *.ts-typed project.
https://github.com/tc39/proposal-type-annotations?tab=readme...
But JSDoc lets you do pretty much everything that isn't a runtime feature of TypeScript (e.g. enums, namespaces, etc). Even generic slots are supported
I found several limitations and already opened several issues on github.
Also, it's not a super limited subset. For instance, you can write types in .d.ts the IDE uses those types in jsdoc out of the box.
If you need precise return typing, conditional types, literal values, etc, you aren't going far if anywhere with JSDoc.
1. not having to type everything (many types are not helpful)
2. makes code less dense (and they can be rendered in HTML) https://x.com/efortis/status/1989776568676221137
3. not needing a compiler (browsers don't support TS)
I think the main jsdoc caveat is around private types.
About 1, IDK if there’s a way to set up TS for that.
https://devblogs.microsoft.com/typescript/announcing-typescr...
But not properly documented (https://www.typescriptlang.org/docs/handbook/jsdoc-supported...), which shows how much Microsoft neglects JS + JSDoc workflow. Github issues have been created a long time ago, but nothing has been done so far. Apparently Microsoft is too busy with their AI slop to work on actually useful stuff.
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.