Top
Best
New

Posted by culi 3 days ago

JSDoc is TypeScript(culi.bearblog.dev)
207 points | 269 commentspage 4
apatheticonion 3 days ago|
Personally, given the limitations of JSDoc, I'd like to see a header-file like definition system for build-less applications (like libraries).

    /
      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): void
MrJohz 3 days ago|
This can be done, and is done in some projects, but the problem is that you need to manually maintain both sets of files, which can easily go out of sync if you're not careful. You also lose out on the ability to check your own code - using the header file you've written there, you can't really check the implementation of `useStr` to check that it works.

The 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.

apatheticonion 3 days ago||
You can define a declaration file alongside its target however the target does not use the types defined within its declaration within itself - only consumers see the types.

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.

MrJohz 3 days ago||
The problem with the header file strategy is that TypeScript doesn't have total type inference. That approach works e.g. in OCaml, where you can (optionally) type the boundaries of the function and everything inside the function can always be inferred. But TypeScript works differently and doesn't support that approach. So fundamentally, what you're describing isn't really possible without using a very restrictive subset of TypeScript that can be totally inferred or having very broad type definitions.

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).

eezing 3 days ago||
Aside from the following, JSDoc is absolutely TypeScript.

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

culi 3 days ago||
Yes, all of these are supported in JSDoc because they are supported in TypeScript. Because JSDoc is TypeScript. You can either define your types in JSDoc comments or in .ts files.

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 file
cutler 3 days ago||
Sounds like Scala type fetishism all over again.
paulddraper 3 days ago||
And in fact, this what the Closure Compiler does…typecheck based on JSDoc.

However, the precision and completeness is not nearly what can be expressed in TypeScript. With generics particularly.

culi 3 days ago|
Care to give an example? In another reply I pointed out how advanced JSDoc syntax's support for generics is.[0] Even allowing for `extends` and default values for generic slots.

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

[0] https://news.ycombinator.com/item?id=46267810

paulddraper 2 days ago||
Ah.....you're putting TypeScript in JSDoc annotations.

Which is a lot different than vanilla JSDoc [1].

I understand.

[1] https://jsdoc.app/

culi 2 days ago||
JSDoc doesn't have a formal spec. Most modern development on JSDoc syntax happens by the TypeScript team. Every release of TypeScript also includes notes about JSDoc syntax.

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)

user3939382 3 days ago||
Agree. It’s superior. I arrived at this about 2 years ago no regrets. Type safety matters on the tooling side anyway. Unless you’re testing for the runtime I guess?
culi 3 days ago|
I'm glad to hear you've had a good experience with JSDoc but I want to reiterate that my position isn't that JSDoc is superior. Just that it is TypeScript
user3939382 3 days ago||
Yes I’m saying, starting from equality Id add on to say it’s actually the better form. TypeScripts extra enforcement adds complexity without usefulness.
casmn 3 days ago||
I am always using JSDocs whenever i am writing a function - i think this is a good practice for every developer - even if it's a simple function.
brazukadev 3 days ago||
I like the mention to someone from the React team as it seems TypeScript/type safety did not help them create better, safer software.
jason_oster 3 days ago|
Writing better, safer software is more of a cultural problem than a language problem. Languages can only do so much.
strongpigeon 3 days ago||
Oh man, the mention of ScriptSharp brought back memories. I started my career at MSFT on SharePoint and the front end was an ungodly mix of ScriptSharp and other stuff.

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.

[0] https://github.com/nikhilk

culi 3 days ago|
From what I've read, many of TypeScript's design regrets have political origins. Enums and other features that oppose TS's structural type system were added as compromises with C# developers in MS and similar negotiations with the Angular team in order to increase adoption of TypeScript over alternatives
pavo-etc 3 days ago||
I would love to know where you read this!
culi 3 days ago||
Oh gosh, I don't think I can recall a specific source. I've listened to many interviews with the TypeScript team and in Q&A they're often asked about their "biggest regrets". Early on TypeScript's adoption was far from a sure thing. After convincing Microsoft their biggest threat was Angular's own AtScript (and maybe even Flow). TypeScript was extremely beholden to whatever Microsoft or Angular devs wanted to be added to the language in order for them to agree to push TypeScript as the future
efortis 3 days ago||
jsdoc is nice because you don’t have to write the non-helpful types.

---

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 pixels
culi 3 days ago|
> you don’t have to write the non-helpful types

This 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.

efortis 3 days ago||
Do you know if it's possible to do that the other way around?
culi 3 days ago||
You mean a TypeScript project without strict mode? Sure. Again, whether you're defining types in JSDoc comments or in .ts files, the behavior is entirely governed by a tsconfig file
epolanski 3 days ago||
Not really, at best it's a verbose and limited subset.

https://github.com/tc39/proposal-type-annotations?tab=readme...

culi 3 days ago||
It's certainly more verbose but certainly not a limited subset. You can copy paste any typescript Type or Interface in a jsdoc @typedef. As I stated in the article, it's still the TypeScript language service that's analyzing either JSDoc-defined types or TypeScript defined types. It's typescript all the way down

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

g947o 3 days ago||
You just haven't worked in a complex enough project yet.

I found several limitations and already opened several issues on github.

efortis 3 days ago||
Although it's more verbose, it makes code less dense because it lives outside the function parameters.

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.

epolanski 3 days ago||
The list of things jsdoc cannot do is long but a simple example is overloading, you cannot express this[1] in jsdoc, and if you need to reference .d.ts files you're back at using TypeScript, so the point of JSDoc was...?

If you need precise return typing, conditional types, literal values, etc, you aren't going far if anywhere with JSDoc.

[1] https://shorturl.at/pg5dL

efortis 3 days ago|||
There a few points in favor of TS types in jsdoc (in order):

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.

g947o 3 days ago|||
This specific one is actually supported since TypeScript 5.0:

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.

afavour 3 days ago|
Eh. I agree with the principle. I’ve written personal projects with JSDoc because I truly love the idea of finally being done with build systems and just serving the files I write without a step in between.

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.

More comments...