Over the past couple of days, an article about the next major version of Svelte blew up on twitter, and caused lots of discussion. The article stat...
Some comments have been hidden by the post's author - find out more
For further actions, you may consider blocking this person and/or reporting abuse
For those wondering how to typecheck js files, use this tsconfig.json:
And you can generate types using
npx tsccommand (remember tonpm i -D typescriptas well).Two main reasons why I prefer jsdocs
1) total control of the final js
2) code is so much more readable
In your travels, you wouldn't happen to have seen any tools that convert between TS and JS + JSDocs? Would be interesting if there were a "lint" step that would allow the preference to be a developer preference rather than a project preference or a code type preference.
I say "code type" as I've found in my projects that my for deployment/publication code feels "right" to be TS as I'm doing other conversions to that code anyways before making it available to others, but the project internal tooling needs to be "at the ready", and JS + JSDocs allows that to run exactly when I need it. Feels sort of why Deno chose to run TS files out of the box, without the TS version match inconsistencies.
It's almost like we're due for a revolution of "responsive coding" where there's enough AST info to allow the user to choose how their code displays as much as how their sites are dark vs light... ðĪŠ
Not that I'm aware of, but its an interesting idea. What you're suggesting would sort of be a toggle between TS/JS+JSDoc, correct? Technically speaking you should have information in an AST to achieve this, but I think it'd be a fair bit of work to make it happen
Yeah, likely. This would be like the number two thing that AST should be used for but no one has time for right after making tabs vs spaces be a developer preference, just the same.
ChatGPT is good at doing this conversion.
This what you're looking for. At least, for turning Typescript into JS with JSDoc comments.
github.com/angular/tsickle
Cool!
In addition to ESLint, and Svelte, thereâs also all the open source work from @iskin (PostCSS, Nanostores, LoguxâĶ) using the same practice.
Bonus: you can test your types using
tsd. See examples in one of my libraries:This can be a valid pattern.
It's certainly something that I do when I am too lazy to set up
swcfor personal projects.But JSDoc does not have 100% support for TS features. It doesn't even have 100% support for type-checking in-editor. I cannot stress that part enough; know that you are getting yourself into the potential of more manual work, when TS features aren't supported, or when you need rigorous type checking, as that is the tradeoff for moving to JSDoc. Also, some of the types you end up writing inline become huge behemoths, if you hope for inference to carry you through the whole codebase.
If you are making a straightforward Java style app, or C style app, where virtually nothing is generic aside from arguments to built-ins like Arrays, or there just aren't any generics at all... and there is definitely no inference at all, then JSDoc can be pretty straightforward. It's not going to hold you to TS strict-mode rules, but it will at least document types for you.
But if you are doing more advanced type algebra, leaning hard on type inference and modern (/ ML-like) type system features, it is brutal to type some of the definitions, importing them from other JSDoc files turns into a foolâs errand, and even if you do get it all, the language server doesn't carry the context from file to file, to hold onto your chain of inference.
I can recommend it for either:
a. lazy personal projects that don't need the TS to be perfect
b. teams that were never using advanced type features, and aren't relying on those types in the doc being rigorously enforced (and doing it yourself; especially across files)
but not for more complex systems or systems with more advanced type requirements.
This is just wrong. It's straight up misinformation. I dont even know what to say. Give me a TS example and I will create you the equivalent in JS/.d.ts files with JSDocs typing and show you the type checking is exactly the same.
Sure. I have wackier examples, and more challenging examples, but a good start would be a 1:1 replacement for
codesandbox.io/s/nameless-fog-9z02...
It's a pretty self-contained, self-tested sandbox.
Oh, and you aren't really going to convince me unless all of those files are JSDoc, not TS.
And the reason for that isn't disingenuous, there is a very simple explanation. A bunch of more advanced algebraic TS relies on testing input types, and possibly generating intermediate types, either locally scoped, or otherwise, and as a space saving measure, things end up utilizing
typeofsome object / array declaredas const. So either that type needs to be in the file with that const, or the type file needs to import JS which is backwards (and would lose context of the key/value pairs, reverting toname: string;instead ofname: "Bob"as JS doesn't have anas constspecifier, and I don't think freeze has been typed to preserve literal values, yet (as many built-in types working with immutable data haven't).Looking forward to seeing the results, though I feel they will not be as terse.
You just gave me like 150-200 LOC of ONLY types. If your entire project is only types, you don't need JavaScript at all, and it wouldn't make sense to not just use TS. I have a bunch of repos where there's a lot of advanced types and I too put them in .TS or .D.TS files because the authoring experience is much better and from an authoring perspective you can't do everything with JSDocs that you can with TS. That said, once we get to the actual JS code that actually runs and does something in a browser or Node runtime, I can import the types into the JS files and be happy. You don't demo your types, you don't do browser testing or integration testing with them, they are just types... you may not even compile them, ever. I don't want to shit on your example, but I really think you are missing the point I've been trying to make about authoring types in TS/D.TS files while keeping your source code (NOT types) as JS files as to not be stuck compiling your code (not types) pre-test/lint/demo.
"can't do 100% of everything in JSDoc you can in TS"
That's not the OPâs position. OPâs position is that they are 100% swappable. If you are disagreeing with this point, then that's fine, I don't have a problem with your disagreement.
I also don't have to do any compiling of that code if I make a type error, the editor screams at me. My types are as tight as they are so that my editor will scream at me.
This example might be trite, but it doesn't stop the fact that if your goal is to make a library that does live API constraint validation, so that, provided an expected data type, it makes you write the correct validators to guarantee the input is correct, that the types which facilitate that are going to be really brutally painful written in JSDoc.
Nor if you are trying to get literally correct types of structs, when reading binary files.
There are literally TS features which are not supported when written in JSDoc comment format. The live server for in-editor type inference will literally lose the thread on recursive types or composed types faster in JSDoc. You might not be compiling code, but if you need to run tsc to check your types as you type, because the type hints and autocomplete are no longer functioning correctly at that particular depth (hence that exact code example), then it is not 100% replaceable. And if your team needs to do these things, or needs to import/export types from JSDoc files into other JSDoc files, because those types depend on
as constandtypeof, et cetera, to guarantee that your type and your dictionary/tree/etc are always in sync, then you will have an abjectly miserable time, relying on your editor to reflect those problems.SWC these days is ~5 minutes of setup for dumping source to output, takes less than a second to run, and does large codebases in fractions of a second.
Im not sure who you mean by OP, but if it's me, then this is wrong; this is not my position, and I urge you to read the blog again.
Also here are your types used in JSDoc
Thatâs a very interesting topic, thank you for sharing. I personally prefer TS over JS in all cases, even for super simple things. I just like static types within my code and donât perceive them as noise. They actually add context to the function and make it easier to grasp the full picture.
But I do understand that itâs a matter of taste, and if someones coming from a pure JS or other dynamic types language, it can feel like unnecessary noise and complexity.
JSDocs seem like a good compromise.
The discussion of ESLint rewrite in JS with JSDocs mentioned at the beginning of the article is also super informative. Iâd like to encourage everyone to read it. Itâs discussing pros and cons of both TS and JSDocs in the context of other big projects.
Thanks for writing the article and taking the time to deal with the inexplicably religious (in a bad way) comments. I've seen dev communities falling into dogma multiple times before (remember the TDD or Redux mobs anyone?) but the toxic intensity of some of the responses is still unfathomable for me.
Its mindblowing
Thank you for your article! For me the switch from Javascript to Typescript has been painful, because it broke the better tooling I had for Javascript, forced us to switch off some useful eslint rules that were not supported for typescript, caused us to talk about which typescript features to use for months which displaced talking about how we want to structure our code (that has serious implications for a large codebase which we have to clean up), but most of all because it destroyed our chances to run without transpilation by using only standard Javascript.Âđ
I had voted for the JS+JSDoc approach, but back then the JSDoc tooling was still worse (weaker typescript support for it), we had strong proponents of TS (itâs strange for me to see that much zealotry outside ethical issues), and now weâre stuck with TS (which â to be fair â many prefer, especially those who only wrote Java before).
Âđ: I wrote down the complete lessons learned in Materialized Typescript Risks â one thing not in this article: Typescript failed to deliver on having fewer bugs.
This is a great writeup!
Besides: like the rest of my site, the article is copyleft (text is cc by-sa), so you can share it as you see fit as long as you link it as source and license what you make with it under cc by-sa, too.
Thank you! :-)
You don't have to. You can write JSDoc types in your source code directly as well, if you don't want to use
.d.tsfiles for some reason. Also the comments are not "possibly" wrong, becausetscwill still warn you about this.We all already use typescript without compilation... Pretty much every modern dev env out there has support for serving typescript code without compiling it first... It's called transpilation: vitejs.dev/guide/features.html#typ... you only build/compile your typescript code when you deploy it to production and that compilation step is maybe 5% of the total build time. bundling and minifying takes much longer.
Not everbody uses vite. Also this still leads to some of the downsides I mention in my post, have you actually read it?
Everything that uses SWC or Babel transpiles your Typescript rather than compile it. Vite is just one example of a build tool that uses SWC. I honestly can't think of any dev tooling that actually compiles your Typescript during development. I just wanted to clarify that speaking about compilation as being a downside of Typescript is a non-issue.
Except its not, the reasons for which are outlined in the post. Also not everyone uses vite or swc.
Everytime anybody gives an example, you jump on it with the same repetitive argument that not everybody uses it... Do you have actual statistics for how many people do NOT use babel AND NOT use SWC?
Because frankly I'm tired of explaining, when people clearly aren't reading the post :-)
This will be my final comment: There are tools, like for example Esbuild (which vite uses internally) that strip the ts types from your .ts source code on the fly during development time. There are also other ways/tools to transform TS to make it feel like .ts runs natively in the browser/node environments. Which indeed some people find to be a nice developer experience, I'm not disputing this, I'm not saying that this is wrong. If you enjoy this kind of development workflow; good for you! However, this way of working still does not address some of the points I outlined in the article, which is why I keep referring back to it.
I have very little experience with JS and TS but I do have a lot of experience with Python, and I keep coming back to this blog over and over again. I think it would be cool if we could write good old non-typed Python, but add fully typed "header files" + include types in docstrings in a way they were understood by type checkers, essentially mirroring the workflow you describe here. The current situation is that stub files were never fully supported by MyPy github.com/python/mypy/issues/5028 and that we resort to brittle hacks to merge signature types with docstrings github.com/tox-dev/sphinx-autodoc-... . I wish we could evolve faster.
In any case, thanks a lot for sharing!
I'll put them in mycomplexytypes.d.ts
Wow. Lots of very opinionated comments here.
I came to say thank you: My team (who predominantly write in other languages, and struggle sometimes with certain aspects of JavaScript) have held me back from adopting TypeScript for good reason: it raises the bar for entry on codebases. That's not always a bad thing, but it is for us.
By using your methods I now have at least some valid way of checking types and it's already revealed some things that were missed in a recent refactor. So, thank you.
Great article!!! Thank you so much for showing us another paradigm, another way to think out of the box!
Definitively there is no silver bullet, neither perfect technique... but as engineers, we need to know all the options, and as responsible engineers, choose the right tool for the job!!
The dogmas doesn't help to grow! If we follow a truth, and became that truth in our only absolute truth in life... that truth becomes a lie, and we become fundamentalist!
Once again, thank you for you bravery to show us another way to think.
Great article!
I don't mind compiling code, but it's nice to have an option; while still getting the benefits of typescript.
Not everybody uses Svelte
What are those worrisome implications? The only one I can think of is the potential performance hit, but Deno now skips type checking by default, so any performance hit in practice is negligible with default settings.
The issue with this is that typescript doesn't follow semver and allows breaking changes on minor/patch versions. This could mean that a package could break at any time when Deno decides to upgrade typescript. The TS versions is defined by the runtime, not by yourself/your dependencies
@thepassle that's also handled (at least in theory, and I'm not aware of any real-world counterexamples) by defaulting to no typechecking.
Per Deno FAQs:
I guess that's already a little out-of-date, as
--no-checkis now default and you instead have to pass--checkto opt in.I wasnt aware that no check was the default now, which indeed mitigates that issue, thanks for pointing this out!
I've edited the post to reflect this :)
This is exactly what makes me think you haven't read the article, the JSDoc equivalent is literally below it.
Very helpful post
Very inspiring post. Just tried out the jsdoc comments in my editor right now. The intellisense and type inference both work fine but it does not throw an error when I assign a string type to a number type.
Here is what I mean:
As you know, if I wrote the code above in typescript I would get an error right away, but it doesn't work when I do it in Javascript using jsdoc.
Anyway, thanks for the post. It was really good
You still have to configure your project to use typescript, or enable it globally in your IDE. There's a good post about setting up this way of working linked above in the post itself :)
I tried what I saw in the link and it works very well. Thanks alotð .
Great :) I was just setting up this for you: github.com/thepassle/uh-oh-typescript
I strongly prefer the inline TS syntax (it's both more readable and more writable for me) but that requires the build step, that's all the matter of choice and enhancing JS with comments and code used only for static analysis makes a lot of sense too
Interesting point. If you don't mind, I have two questions:
When you say you maintain large projects, approximately how many files does this mean?
Does using Typescript in this way enforce type checking? If someone makes a typo despite the Intellisense, does anything stop it from being deployed to production if the transpilation step is omitted?
tsc --noEmitand it'll error if your types are not correct, and prevent you from releasing it / merging it to main branch.Hi Joren. Thanks for replying.
I asked about the file count because the article includes the line "As someone who maintains many projects at work (some of which are large)..." where "large" is emphasised in italics. That suggests to me that this was significant to the point being discussed, and I wanted to understand this further.
Large is also relative: a large t-shirt bought in one part of the world might be a different size from another large t-shirt bought somewhere else, or even just in a different shop/store.
As you asked, a number that gives me a rough idea of the project size would satisfy me. Saying triple digits also satisfies me and I understand if this is a sensitive or confidential statistic.
This is a new approach to me, and I wanted to learn about the scalability of it. That is all.
Thank you for your note on
tsc --noEmit. As mentioned earlier, this is a new approach for me; I asked the question as this information was not discussed in the above article.I find myself exclusively using JSDoc these days because the code I am working on now, is all vanilla js. There's no build system or anything. The code you write is the code that is send to the browser directly.
For the most part it's great. The main issue I have is with doing a lot of things in
lib.dom.d.tslike querySelector.If you
querySelectora string found inHTMLElementTagNameMapit knows how to interpret the return value.But more often than not, for this any many other things I'm finding myself having to add
/** @type {HTMLElement} */various places in my code.(Perhaps, some of these things would be an issue with Typescript too)
with swc and rust-based dynamic compilation, typescript adds milliseconds of compilation time no one would notice.
It is kind of moot why they would do this. Unless they never plan on including swc or using deno for their tools.
Correct me if I'm wrong, but with JSDocs if some dev misses an error in the editor he will be able to push the code, right? While with the Typescript the errors will be catch in the build time, right?
Nope
Can you please elaborate? What am I missing?
You still use
tscto do typechecking. You just skip the transpilation step. You can also do this in your CI or during the build to ensure your code stays typesafe. Typechecking is still done by typescript. You skip the compilation step.Oh, I see. Yeah, we can set up
tscin the CI to do the type-checking.Of course, that would make the difference between using JSDocs and Typescript smaller in terms of build time.
I agree with you, it's a matter of preference. I personally prefer to use Typescript directly, I think it's more readable and easier to maintain.
Anyway, thanks for the article, it was fun to read and an interesting perspective on the topic.
Agree with the "it's a preferences thing" but it It looks a bit old school to me to be honest.
Kind of using a separate header file in C/C++
The educator you're hinting at in the conclusion did nothing to say that JSDoc wasn't capable of this, nor did they attack the original idea of Svelte migrating away. They wrote an original tweet expressing confusion over the article, used some terminology which you misconstrued into an attack against Rich.
They even clarified later that they did not intend to come off as you'd read them to, and expanded on their intentions and learnings:
twitter.com/jutanium/status/163983...
It's you who has decided to:
I think that says a lot more about you then it does them.
I do not understand the benefits of deliberately disguising the titles. If it were TypeScript^TM, then you either need a transpiler or a new JS implementation.
What you do have is an alternative type annotation system for javaScript, so why not name it honestly like that?
Once this is done, we can also honestly discuss about the differences, advantages and disadvantages compared to TypeScript. It is true that you can fully check the types with your special linter. It is also true that, e.g. the browser JS-engine can interpret it without compilation/transpilation. But honestly, if you write a project with more than 100 lines of code, is the loss of a fast transpilation step soo big that you want to get rid of it so desperately?
As someone experienced in using strongly typed languages, I can say that using type annotations first-hand makes you think about algorithms quite differently. The extreme is certainly Haskell where people are reluctant in writing documentation and instead spend the time on finding expressive names for functions, variables, types and typeclasses. The Haskell compiler may be slower than the babel transpiler, but usually the development time is not wasted during compilation, but on working out types after writing down your idea of an algorithm.
(Oh, and Java is not the archetype of a modern strongly typed language. Contrarily, Java is slowly picking up convenience from languages such as Haskell, Kotlin or TypeScript.)
If you write the ".d.ts" file before writing the implementation, then you may make things easier, but if you are to write the type specifications after the implementation, then you will have to think about the implementation twice. In the latter case I will predict that type annotations will always be handled as second-class citizens, i.e. only if and as much as you really must do them.
Of course, it's a preference... but imagine someone migrating from Java or other typed language. It's better to learn typescript than javascript plus jsdocs.
For me, it looses the best of typescript. And also, we have infer typing that should be so complex to recreate using comments.
And also, the jsdocs is much more extensive to code.
Compilers are done to avoid it. Or do you planned to put on production your code with comments? no? then if you have any step between code and production, you have a "compiler"
Read the article again
Where it doesn't do harm ð
for a community that doesn't have a strict types system for 25+ years, typescript users is so madly obsessed with types
Rich Harris said that a compilation/transpilation step is more or less mandatory for Apps anyway (optimizing, bundling etc) but the same is not true for libraries
Projects can choose to do as they please but the pros of "not compiling" don't outweigh the problem being introduced here.
When you write TypeScript using it's type declarations IT IS your source of truth.
When you write JSDoc and implement alongside the declaration you now have two sources of truth.
This for me far outweighs any gain. Introducing multiple sources of truth like this for a large codebase just makes them prone to bugs and fragile for new developers to get started.
This is wrong. Your types are still the source of truth.
I think you've missed the point, you can quite easily abuse what the JSDoc states and what is actually written by the engineer.
JSDoc

vs
TypeScript

By the time you add some form of pipeline w/ linting on top of the JSDoc approach you are already reliant upon a toolchain, you may as well just compile from source ðĪ·ðžââïļ
Each to their own though, hand craft it if it makes the engineer feel like they have more control and have reduced headaches. But from experience working on multi million LoC web apps now, across multiple teams, enforcing engineers to write JSDoc always and maintain it would have been a nightmare.
I'm not sure what this is trying to show? You are running TypeScript here, you're dependant on
tscwithout relishing any of the benefits like type declarations that are transformative but removed after compilation e.g.Omit.Have you actually read the article? The point that is repeatedly made is that you're still using tsc
good
Just like PHP! ð