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 tsc
command (remember tonpm i -D typescript
as 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
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
swc
for 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
typeof
some 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 const
specifier, 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 const
andtypeof
, 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
You need to read this blog again.
Not for writing your types, for writing your source code they are but some types will need to be written in .TS/.D.TS and imported in JSDocs in your JS source code.
This is blatantly untrue. The exact same logic is used by TS language server, your editor will scream just as loud if you use JSDocs + JS file versus TS file. Your editor does not "lose the thread faster" or do worse at typehints and autocomplete. OP has proved this fact multiple times in the replies, but here's another example:
I used this example because it shows how to import from a .TS/.D.TS file and then consume those types in a JS file, and the editor is definitely screaming at me for not adhering to the types. It doesn't show it in the screenshot but the typehint/autocomplete works 100%.
So hopefully I've convinced you that you are wrong about the types not working as well, it is absolutely identical. However, I didn't need a compilation step to run my JS code, because it's JS and it runs without one.
That's 5 minutes too many. And no, if you have testing, demoing, linting and you use custom tools for it, you first need a TS-loader for all of them, which will undeniably take longer. Great that you have a setup with all batteries included that works for all your projects, my projects require more custom setups which need to adjust often and I can't just take a Vite or SWC or whatever template and just assume that it'll be good enough for me.
SWC these days is ~5 minutes of setup"
Lol beat me to it
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.
The example wasn't made for this purpose. The original example was made back when I was teaching a team how to use the (relatively new at the time) template literal types, to dynamically create keys from known (externally validated) input types, to a dictionary lookup, which were statically provable, and carried inference all the way through, once past the data service responsible for calling the API and returning a valid response. I coupled that with recursive types that were being used in a dynamic layout generator, based on input as an n-ary nested tree of known node types. This example, funny enough, solves for both template strings, and recursion. Go figure I would pull this out when claims that JSDoc is a good authoring / user experience for these types of things.
This is a self-contained, dirt-simple example to pull out, which has instant real-world application with only trivial changes.
One such example would be statically converting CSS to JS and back; another would be pascal-case to camel-case to serpent-case and back, to provide immediate developer feedback on errant style input. Yes, that requires a runtime test as well. What's your point? You think this can't be made useful with little more than an if/else?
The code that dynamically built the lookup keys and ensured proper access was preceded with what was essentially a precursor to Zod; a micro-library that when given a type as input, would force you to write a set of nested guards matching the shape of the object, which all rolled back up to one boolean, representing the conformity of the received input data, along with helper functions for composing those tests together, via boolean operations, and automating the casting of testing functions into type guards for the type that was inferred by traversing that far through the tree. This was now half a decade ago. It had a sister library intended to do the same thing, but respond with all validation errors sorted by the paths taken through the tree of data, rather than with a type guard representing the validity of the tree.
Would you have preferred that I updated and provided the code for that library? I mean, I can update it for a post 4.0 world, but then I really do expect to see 100% of everything rewritten in JSDoc.
Why would you presume I'm unfamiliar with any of that. It is, in fact, why they were two separate libraries which could share the same composed test functions; one library was for quick validation to determine whether a data service could successfully parse data to then be transformed into an internal domain type... and the other library was for identifying causes of errors, at some point after the initial callstack was exhausted, so that string-building and error-reporting didn't eat cycles that should be reserved for either recovering or notifying the user / calling client.
It really isn't. If it is an arbitrary string like in this example, sure. But aside from CSS to CSS-in-JS, another practical example comes from the DataView component which has buffer access methods like
getUint8
andsetFloat32
.JS has the Uint8Array constructor and the Float32Array constructor, whose name properties, funnily enough, are
Uint8Array
andFloat32Array
. You can see how a simpleget${Arr.name.replace("Array", "")}
would actually give you a valid method for each of the supported data types... but you should probably also see how TS would not be cool with you doing that in strict mode. I'm not saying that this is an every-day event. Nor am I saying that everyone on the team can / ought to be doing this type of problem solving. I am saying that for cases like these, writing these things in JSDoc is painful.It's not about automatically casting to a domain type, it's about being able to validate that the input matches the expected input, and thus can be cast to a domain type. And this is again, not a regular thing, but going through multiple layers of transforms from non-aligned, packed binary data from 1996, that loosely conforms to a spec, to data that can be worked on in modern JS, to data that can be loaded into a WebGPU compute shader, to data that gets bound to vertex / pixel shaders... having clearly defined domain types, as you move from layer to layer helps to know what you are working with at any given point in time.
But it can do. It's Turing complete, the only reason it "can't" do, is because the Language Server is only bound to the code editor output. If there were bindings you could use to declaratively dictate what external code the Language Server should run, based on some command buffer spat out by a type, then the TS type system would essentially run like a Lisp that takes action on the top-level return. Again, not saying it should. But it can. If JS wasn't provided any bindings to browser objects, document objects, the terminal, the filesystem, et cetera, then it couldn't, either.
You would have been better off making a smaller example that exercises the type system in ways you believe you're not going to be able to do in jsdoc.
The example you linked is pretty useless without the sister library you mentioned because except in EXTREMELY simple cases, typescript won't/ can't marshal a parameter of say String into the more restrictive types even with proper guards in place. I'm glad you were able to use a sister library to facilitate that, but that is the sort of additional detail that is crucial to an informed critique / analysis of an argument.
Okay, so the point is to demonstrate the template types and type recursion. That is doable in jsdoc today with the correct instrumentation. while codesandbox.io is missing some of the features necessary, desktop vscode is not. As proof, I have authored the following repository github.com/Trapfether/jsdoc-TS-equ.... Check it over yourself and verify that the types match yours exactly. No tricks, no hacks, no slight-of-hand necessary. Just a properly configured tool. JsDoc has been made nearly as powerful as typescript proper because the typescript team has put massive effort into doing just that. They along with the JsDoc, and visual studio code teams have patched the vast majority of the holes there used to be.
In the few instances where you literally cannot express a concept in jsdoc, then you can quickly author a .d.ts file and move on with your work.
There are legitimate reasons to pick typescript proper. There are concepts that exist in typescript CODE that don't exist in regular javascript because TS constructs additional functionality to facilitate such things. No-fuss enums being one particular example. However, those are reasons to use the typescript LANGUAGE and compile / transpile it to js. Typing is not one of those reasons any more as they have worked HARD to make jsdoc work nearly identically to typescript typings itself.
This is all to say that while you might find a niche use-case for compile-time string literal validation, you don't need typescript proper to do it. js + jsdoc with typescript checking your types will do a bang up job.
I wasn't looking for an "informed critique" of my code or the usefulness thereof, I chose this example for the express purpose of choosing something difficult to express and write and maintain using only JSDpc comment notation. Why? Because TypeScript is about types. Yes, I realize TypeScript has enums; it would be trivially simple to point that out. Also, enums in TS aren't great and come with a bunch of caveats. So have the decorators that have been around since Google agreed to use TS for Angular 2 instead of their "AtScript" with annotations that they demoed circa 2014. And?
More useful is a statically known recursive descent layout generator that takes an AST of nodes and their attributes, and a dictionary of generator functions, and showing that the whole thing can be statically known as you build, to prevent node types from being given incompatible children on the CMS side, or to recover from it on the client side.
Or a validation / marshalling library that does static validation and then marshalls to a domain type, rather than just a "trust me bro" cast.
If the VSCode team has put in the work so that this is 100% viable today, in JSDoc comments, then congrats to them. That is a big accomplishment, and I am glad Microsoft let them carry that out to completion. That's grwat. Mea culpa. That wasn't the case even 2 years ago, though. It would fail on asserts and revert to general types. And this post isn't "JSDoc has come so far from when the VSCode team started work on it, years ago" it's "consider moving all of your TS to JSDoc".
Again, this isn't about "how useful are these lines of code", but rather "how likely is the language server to choke on these types, and if so, does that signify that it is not ready to handle the more demanding cases". Because it isn't about publishing this code, it's about publishing medical devices, or financial applications or logistics devices with confidence that the developers aren't going to be allowed to make mistakes resulting from the type system going on vacation, or the dev just flippantly bypassing the types and doing what they are going to do, because "it's just JS".
You posted a link to a code sandbox that spent 174 lines to do nothing but constrain the content of 5 constant variables to be a specific value. When it was pointed out to you that the usefulness of said code was less than questionable, you THEN mentioned the usage of an additional library that would facilitate marshaling a less-restricted datatype into the expected restricted type. You did not show off any of the useful demos you have since referenced in the original post. So you should not be surprised that several people (including myself) took one look and decided it literally was not worth our time. THAT is what is meant by informed critique / analysis. I used both critique and analysis for a reason, disjointing the two creates a different type of statement (something you should be particularly pedantic about considering the content of your comments).
Being able to describe in types the valid syntax of various recursive IDLs I can see the value of, especially when paired with a code gen that can take care turning your types into a parser and or validator for incoming data. Being able to describe in types recursive tree structures is also useful. You demonstrated neither in your original link containing comment.
Finally, I believe you said this in your earlier comment:
"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 did just that. I'm waiting.
This was never the point of this post.
I didn't, actually. I mentioned that this was a several year old code example, that was moved to this sandbox .. a while ago, and its intent was to demonstrate both recursive types and template literal types, because both were poorly understood by the group who required them. And it's not "constraining 5 strings", it's arbitrarily statically providing the correct (if simplified) title-casing for a myriad of input in plain English. That it demonstrates the point using 5 strings or 10 strings is inconsequential, and you could literally type in a title, copy and paste the type hint, and now have the correct casing (unless you expressly want Strunk and White, Chicago, or the like, which you could actually reconstitute by recomposing some of the higher-level types to appropriately meet the needs of the given style guide). I only mentioned additional other code, outside of this perfectly self-contained sandboxed example code, when it became abundantly clear that absolutely everyone is missing the point of me using exactly the type of code that would break in JSDoc inside of JSDoc, regardless of what other surrounding code there happened to be. See, normally, people want isolated usecases that effectively demonstrate the problem in a vacuum, and I have a number of premade examples.
This is valid TypeScript. It literally does not matter what the purpose is or how many lines of JS actually run. It is TypeScript, and this kind of aglebraic TypeScript does, indeed, have many practical purposes. This particular example, as previously stated, is not, itself, a practical purpose, despite using tools which, themselves, serve practical purposes. "Well, you should have made an entire IDL, an entire recursive descent generator, an entire tree of valid input data, and an entire map of node->layout transformers, and constrained the whole thing to 50 lines in the same file, when part of your concern is JS-to-JS imports of types, expressly for the purpose of demonstrating the issues you have" is not an ask I would expect to have levied if I went to, say the TS Language Server team and said: "Hey, I'm having issues with type recursion, here is some type recursion that is known to break". "Well it should be running more JS" has literally nothing to do with the problem I mentioned, and is a complete non-sequitur.
The quality of the code, the authoring style, et cetera, have not really been critiqued. The analysis of, say, runtime performance of the in-editor language server, or type correctness, has not really been provided. The only analysis that has been provided is lines-of-code and number of lines of running JS, and the only critique provided is "this problem is not worth my time". Which is fair, but if JSDoc is going to solve all of these problems, then perhaps actually demonstrating that would be helpful, especially when someone who previously had experience with earlier revisions of this tool, for this purpose, is providing examples known to break in earlier versions of the tool. Even just a blog post with a codeblock that has live type inference, a la the TypeScript sandbox editor, to teach these concepts, would be a valid usecase for this code, given that this is from a sandbox that was literally used as an advanced demonstration of the synthesis of these features, highlighting real-time editor feedback of the types. Unless your argument is literally that if it's not production code, it's inherently worthless, this is useful code within the context it was produced and used.
Demonstrating that JSDoc is now ready to handle advanced TS usecases, when it previously wasn't, doesn't only apply when the exact code-snippet provided is deemed "useful", for your particular definition of "usefulness".
If you would like examples which need to be further updated for the current versions of TypeScript, because they were written with ~3.7 support in mind, and before most existing codebases even had access to
unknown
:github.com/seanmay/io-guard
have at it. There's another library that also could not be written in JSDoc even a couple of years ago, with expectations that it would function to spec, in editor. And the in-editor experience was the entire point of the library, by forcing you to write tests that conformed to the type and existence of the value, regardless of simplicity or complexity of said value. Does it have use? I don't know. That depends on the user. Someone using Joi and blindly casting on the other side probably thinks not. Someone using Zod these days probably thinks not. Someone who types
JSON.parse(x) as any
definitely thinks not. But I'm not asking whether the library is currently needed, nor worthy of VC funding. It served its purpose in a time when nobody else was providing solutions to the problem meaningfully. And so the point isn't "would you donate to this", the point is, "can JSDoc handle the esoteric nature of some of this, because before now, it really, really couldn't". Moreover, it isn't about answering "well, why would you do it this way when you could just use yup or a single giant function with typeof checks, or why don't you do it this way, instead" like... yeah, the point is not "can I write code the way you write code, and therefore conform to the exact style needed for your claims to be true" it's "can JSDoc do exactly what I need it to do, regardless of what that thing is". I can't believe I have had to say it this many times; people's lack of ability to extrapolate use out of intentionally self-contained, sandboxed examples is missing the forest for the one single tree of "but this exact example of 100% valid TS doesn't run JS code, so none of these concepts can possibly be valid or useful in any other way, and thus no JSDoc writer would ever want them to run in JSDoc" and is myopic at best. Curried functions which rely on type-inference of constrained generic types within the parameters of the returned partially applied function also had problems in JSDoc, but I am indeed terrified of the nature of the conversations had once I provide examples of those, beyond what is in the repo.Yes, you did. To the VSCode team, I say congratulations for fixing the myriad holes that prevented this from working, previously. And to you, congratulations for proving to me that my views on JSDoc's capability as an input source to the TS language server are outdated, and for doing what I could not do, all those times before. You even did it without impacting the code style too deeply, so kudos for that, as well.
Here is your updated challenge github.com/Trapfether/io-guard-jsd....
The editor experience is as you would expect from typescript. Note, I did not bother with getting tests to run as that is unrelated to the challenge. You would have to change to jest-babel since jest does not support esm imports natively. The tests will fail with "SyntaxError: Cannot use import statement outside a module" despite obviously being modules.
This was completed as the last one, no .d.ts files, no hacks, no tricks. The OP never was of the position that you should avoid typescript files all together, he has repeatedly stated that using .d.ts files is a-okay in his book. I hope that my work here demonstrates to you that the need for even that is greatly diminished compared to even a few years ago.
The point being made about your original post is that by all accounts, until you expanded on why that code might be useful, it appeared to be an esoteric slapstick attempt at a gotcha. A waste of time. You can absolutely do things in TS that cannot even now be done is JSDoc in the same way. That by no way means it could not be accomplished in some other manner, or with a different tool, library, harness, etc. The original post looked much closer to a troll response needlessly contorting the type system to accomplish a ridiculous goal with substantially easier solutions. It was only AFTER you supplied additional context that reason for such contortion was made apparent. Had your original post done even a subset of the useful functions you outlined afterwards, this perception of being a troll never would have fallen on your post. Even a modest example of guaranteeing the content of the string was valid XML instead of a specific string, or as I said in one of my earlier responses, just typing the string such that is HAS to be title case. A slightly more complicated version would have forced the "A" article to follow the rest of the title as is common when features are displayed in a list. I'm sorry that I thought you were trolling.
None of this is to say you should / can abandon typescript. If you're using some of the features not available in regular javascript and they legitimately make your life better, more power to you. If you are shipping a consumer-facing app utilizing one of the major frameworks, then you are already bundling, tree-shaking and packing; throwing in a compilation or transpilation isn't going to really hurt things. If the ONLY reason you are using the typescript LANGUAGE and therefore undergoing a transpilation / compilation step is for robust type safety. That is no longer NECESSARY, there is now an extremely viable alternative. I hope this has been eye opening for you. Have a great day.
Ugh, sorry, the dev.to UI for replying to comments/threads gets a bit messy with so many comments and replies, so I think I screwed up somewhere.
devblogs.microsoft.com/typescript/...
I’ve actually been excited for real work to be put into trying to flesh this out. One major concern is not stepping on the toes of potential future JS syntax, but regardless, having the ability to tag / annotate and have those annotations ignored at runtime would be all kinds of happy-making.
It also sounds really dangerous, because shipping literal typescript to the browser gives Microsoft leverage against all browsers: they can add things to TS (that they control) which are only supported in edge (that they control) and other browsers have to follow or require a transpilation step that Edge does not need — pushing devs into using Edge.
No, I don’t trust them.
Though if my tooling were to update (once the proposal reaches stage 4), I might regain my better tooling that way. Which would reduce the pain of TS a lot.
But the spec is about ignoring type annotations, and doing so in a way that TS and Flow and Hegel and others would all just run in-browser, because all of the type systems would just be ignored.
I wouldn't put it past any browser manufacturer to go off-spec, but if they did that, they would be begging to have Flow files just take down thousands of websites in their browser, when they try to do some proprietary thing and explode on similar but marginally different syntax.
"explode on similar" — that sounds like the third step: (1) Embrace, (2) Extend, (3) Extinguish.
But the other way round: provide some extensions that only work with their browser. Do you still remember the "works best with"-tags?
First marketing that as a pure devtooling thing (you can simply copy the code into the browser console of our browser) and once it’s supported in more browsers make it mandatory and add support for doing special stuff when in their browser. Finally changing the language spec so updating to the newest typescript only works on browsers that keep up. This enables them to force other browser vendors to play a constant game of catch-up, because MS controls Typescript, so they can add their plans early in Edge and make the design match the features while others have to retrofit them in a hurry to stay competitive.
Internet Explorer extinguished through monopolistic practices in marketing and availability, not through making the internet break. The breakage was because Microsoft would add new, nonstandard features or implement new nonstandard features in their own way.
It's not like Edge would release an update today that made all jQuery pages break. Do you think everyone would move to Edge in that case? No, they would stay on Chrome where they already are (and given that Edge is Chromium it would take a bunch of work for Microsoft to retake control of all of the stuff that they just relinquished control over a few years ago, like the JS runtime and the viewport renderer, specifically).
The spec would allow for a flooding of the internet with not only TS pages but also Flow pages. If all of React just died, because it still internally had published Flow annotations, do you think people would be flocking to Edge in that case? It would be a death sentence.
Edge currently has ~10% of the desktop and ~0% of the mobile market, I believe. How, exactly are they going to exert this monopolistic control, especially considering that it's a Chrome browser with a coat of paint and some usability features on top.
As you wrote, the codebase is the one of Chromium — Edge adds features on top and has a dev team that can change Chromium itself. That’s the market power, definitely not only 10%.
Yes, exactly that. Imagine what would have happened if Microsoft had also controlled not just Frontpage but the dev-tooling used by 70% of web developers.
They don’t need to retake control of what exists, because they have control over what’s added.
And React does not have to die. It suffices that it’s just slower. Ahead-of-Time-Optimize for datastructure-definitions from Typescript types, but not from React. Or only allow usage of certain Edge-specific browser-APIs if typed with Typescript. Nudge people into Edge. Make React apps just somewhat less usable.
If you think that can’t happen, look at how part of this already happens with frameworks optimizing for Chromium, but not for Firefox. This is without having full control over the language used.
But then this is just what happened many times before. It’s not guaranteed to happen again.
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.
Ok so you found a complicated way of working with Typescript, that makes it slow in development, which is why nobody uses that way in the first place and now you want to find a solution for that problem that doesnt exist?
its literally not complicated :')
Yes your solution is less complicated than the complicated way in which you suggest people use typescript except... nobody is using typescript like that in the first place... I've been telling you this the whole time, you're solving a non-existing problem.
This is such a non-argument. As mentioned in the blogpost, which I doubt you've read, because you keep missing points that are addressed in the post itself, I very happily use this approach at work.
I'm gonna leave this discussion at this, I have some JSDoc comments to write :)
You go on and on about your approach. I have not made one comment about your approach, the only thing I have been telling you this whole time is this: nobody is compiling typescript during development. Maybe you should read the title of your own article...
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-check
is now default and you instead have to pass--check
to 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
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 --noEmit
and 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.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.ts
files for some reason. Also the comments are not "possibly" wrong, becausetsc
will 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.
Yes, sorry. Wrong example.
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
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
tsc
to 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
tsc
in 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.
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++
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
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.
for a community that doesn't have a strict types system for 25+ years, typescript users is so madly obsessed with types
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
tsc
without 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
The entire point of this whole article is that both approaches use TypeScript Compiler to do typechecking and that the type safety and IntelliSense is 100% equivalent. It is literally beyond me how people can miss that when it's repeatedly stated in the article, with massive emphasis.
Because there is literally 0 requirement to run
tsc
for any reason, whatsoever, from the standpoint of the author of the code...You now have a necessary build step to catch the errors that the developer made in JSDoc, but you have to do it in a post dev process, if you aren't going to run a live recompilation step... and if you are going to run a live recompilation step, then you are just adding more headaches because you are inventing more ways of writing your type incorrectly, without being caught by the editor (and instead requiring debugging via the terminal, or debugging via a precommit hook, or via a PR process). You went through the whole trouble of setting up the whole buildchain (at least from dev to origin) but completely skipped out on the benefits of great type inference and advanced type algebra being vetted in real-time, with immediate dev feedback. So you are still doing 100% of the work but getting ~40% of the benefit, with the added danger of being confidently incorrect about your types, and the added frustration of getting your feedback in a different place than your text buffer, and you are even adding the extra timebomb of allowing untested JSDoc through, given that it doesn't need any part of the build step to function.
I am going to repeat this 3 times in the hopes that it lands, because people keep missing this despite the author emphasizing this point multiple times:
Real time type inference, editor feedback, Intellisense, is completely equivalent to using TS files.
Real time type inference, editor feedback, Intellisense, is completely equivalent to using TS files.
Real time type inference, editor feedback, Intellisense, is completely equivalent to using TS files.
There is no need to run TSC to see the type errors, in VSCode and Jetbrains editors, Typescript Language Server works with both TS files and JSDocs in JS files, in the EXACT same way, to provide this live feedback, all you need it to set allowJs and checkJs to true in your tsconfig for that to happen. There is 0 difference. ZERO. There is no need to run tsc --noEmit during dev to lint, because you get the feedback immediately in your editor just like you would if you were using TS files.
You run tsc as a linter.
good
Just like PHP! 😉