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
Code is more readable... the Jsdoc of complex types certainly not
I'll put them in mycomplexytypes.d.ts
Which is... Typescript
Where it doesn't do harm 😃
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 not true, gatekeep-y and bait.
There is literally nothing gatekeeper about it. I am a huge proponent of TS, and of ML-flavoured algebraic TS.
https://codesandbox.io/embed/nameless-fog-9z02xw?file=/src/index.ts&codemirror=1
If you can rewrite this using only JSDoc .JS files (no TypeScript definition files, for actual practical reasons), without changing the file/folder structure, I will tip my hat to you, because I have tried on more than one occasion.
I have literally been working on personal projects trying to create literal types of structs read from the Quake 1 binary files, with not only known number types, but known byte-lengths and element counts, through tagged typing. If you want to show me how that's a smoother experience in JSDoc than spending 15 minutes setting up electron, an SWC backend build, and SWC frontend build, a chokidar watch on asset files / client code, and a two-line preload script to hot-reload, so that the app can run either in electron or in browsers with WebGPU support... be my guest. It is a hard problem to solve once you get into data that doesn't align like modern data, and types which dynamically expand into different types as you continue reading the file data.
Start by making a practical example. Then people might actually regard your input with some amount of weight.
You used 174 lines of TS to guarantee at compile time that 5 strings in your own source code had a particular value. You would have ironically made a stronger case if you had simply demonstrated usage of the TitleCase type itself. Being able to guarantee at compile time that the content of a string literal has a particular value is interesting, but irrelevant to daily usage. Those types give you no runtime guarantees. If your app is taking data from a user, api, or database, you will have to create or use a validator to make sure the incoming data meets those expectations. If typescript gave you the ability to automatically create and utilize said validators, that would be one thing. It does not. You can use code generation tools to automatically build these from your types, and deal with the cryptic runtime errors when a piece of data does not conform to your expectation. Or you can write a proper validator that directly outputs the desired type and throws discernable descriptive errors when it is not possible to do so.
Being able to say "these next fifteen bytes constitute this type, of which the first 4 are this other type, etc" is again interesting in principle, useless in practice. You will have to validate the data when it is read in anyway, and can tag the types as part of the return.
If instead you would like to use these as type guards on a function call or when you intend to write INTO a buffer, then you have moved the issue back further into your code. Something somewhere will have to validate that the data actually matches your expectations prior to you outputting it. There is some marginal utility if the data is wholly constructed within your code with essentially zero input from an outside source, but then you can constrain the types anywhere along the chain, not just the final function call.
The reason why it took you 174 lines to do in types what would take just 14 in a function, or 5 if you just use const variables and be done with it, is because you are misusing the type system itself to do something it can't actually do. It has no runtime checks, it cannot verify that the data you just grabbed from the database is actually in Title Case, it can't verify that those next 15 bytes represent what you think they do. You're example is an attempt to represent a compile-time parser / serializer which does not function.
const SlayerTitle = "Doom";const PinkFloydTitle = "Several Species of Small Furry Animals Gathered Together in a Cave and Grooving with a Pict";
const StephenHawkingTitle = "A Brief History of Time";
const EricBogleTitle = "And the Band Played Waltzing Matilda";
const RedHotChiliPeppersTitle = "Under the Bridge";
Look at that, not only did it not require Typescript, it required zero type assertions at all since the const strings have automatically inferred types.
Begin by making a practical example, then someone might take you seriously.
I didnt advocate using only JSDoc. Im advocating using .d.ts files in combination with jsdoc.
I'm unsure if you meant to reply to me. I was replying to Sean May on why their example code was ridiculous and not close to what you would use for practical code. That is why no one even took the time to attempt converting it into just jsdoc or jsdoc with .d.ts files.
I was telling him to use a practical example if he wished to have people take his critiques and challenges seriously.
All that said, 90% of the type alchemy he did is possible in inline jsdoc with realtime ts type checking. the few holes are easily patched by .d.ts files as you stated.
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! :-)
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.
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!
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.
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.
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
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 :)
Very helpful post
The irony is: There is no Svelte project without compilation step.
Not everybody uses Svelte
Sure, but, take a look at the very first sentence in your article... There's some irony in there. I get how and why this 'compilation-less' stuff works and especially makes sense for library developers. I for one would not want to have to maintain (at least) 2 files plus additional (possibly wrong) comments for every single module. But as you said, it's a question of preference.
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.This is exactly what makes me think you haven't read the article, the JSDoc equivalent is literally below it.
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)
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 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
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
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.
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.
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.
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.
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++
for a community that doesn't have a strict types system for 25+ years, typescript users is so madly obsessed with types
Just like PHP! 😉
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