WTF. What kind of project would take long to compile from TS? Windows 11 (if it were TS)?
I had a long time personal project that was several megs of code with several hundred types/interfaces. It took 13 seconds to compile. If your application takes hours to compile and is less than a petabyte of source code you have some catastrophic mistakes in your approach.
Just think if you had to take a big JavaScript library, say the size and level of dynamism of jQuery, and manually write annotations for it yourself.
It would indeed take you hours to cover everything.
BTW, jQuery is pretty small. With good lint rules you could probably manually type everything without missing anything in less time than you think. Whatever time is spent doing this manually apparently is worth the cost savings comparing to long compile times if takes more than a few seconds. You only need to write the types once and then again on major refactors. If your builds just take seconds you can run them dozens of times a day. If your compile time is hours then whatever automation you imposed to write your types or d.ts files for you is entirely self-defeating as its costing you dramatically more than just doing it manually one time.
When you have a large project types are written as the code they describe are written, so its little more effort than typing on the keyboard in a separate file.
There are cases where folks write them by hand, but that's not what I had in mind when writing the article. I've rephrased that part a bit to hopefully make it less confusing.
How would a project configure node and typescript to use a npm module that has exported .ts files?
https://web.archive.org/web/20240706220437/https://marvinh.d...
I've really enjoyed this author's series on optimization techniques explored through non-trivial yet still small case studies, the other articles linked near the top are all worth checking out too!
Happy to hear that you like the series!
Isolated declarations should allow parallelization and faster type checking with tooling that supports it. This shouldn't be changing your build/release process or what you export in your package.json.
If you also happen to use the jsr registry to publish your package, it sounds like you can update your package.json to export TS files and they'll compile/inject the JS into your release artifacts on publish. Not sure if this feature requires isolated declarations though.
For Node users the isolated declaration feature won't do much other than speed up the creation of the .d.ts files a little. For runtimes that can run TS natively like Deno (disclaimer: I work for Deno) the benefit is much more apparent as you always want the original TS sources, but can generate .d.ts files on the fly for faster type checking.
EDIT: Formatting
I think the author seriously exaggerates the difficulty of creating type declaration files. It's literally one boolean setting in your tsconfig. I wish they would have given more time to proving the alleged pain points with some examples rather than just telling us how great this new feature is. I personally don't see how this changes my life at all, and I'm a full time JS/TS dev.
Node 22 has an experimental flag that supports requiring esm as long as there's no top-level await
No. It seems like they are assuming the user is using JSR, which handles generation of JS and d.ts for you. But then none of this would even be a problem.
The NPM registry doesn't do any of that. You could absolutely publish only .ts files to NPM and then use a post install script to generate JS and d.ts files, but that is a pretty terrible idea. Even if the process is super fast, it's still an operation done potentially millions of times, versus an operation which can happen once at publish time. You could also assume the user will have their own mechanism for compiling TS or consuming TS directly, but this is even worse.
In Deno (disclaimer: I work for Deno), which supports running TS natively, packaging outside of npm has always worked by shipping the TS files directly. Transpiling them down to JS files is only something you ever had to do when publishing on npm.
So far the problem that a dependency only compiles with a certain version of tsc hasn't popped up since Deno was launched. But that may have been because Deno always ships with latest TS and Deno users don't hesitate to update as we haven't done any breaking changes so far.
It's easy to get started with JavaScript, but to squeeze every bit of performance in JavaScript will quickly get you into a very complex topics that are not documented and change may change from version to version as they are often not "contracts".
The only way to learn those obscure topics is by reading the source code for your JavaScript implementation, their bug tracker, etc. This means if you have not been reading C++ or executed a non-JS profiler to make your JS faster then you probably have not gone far enough.
There are unexpected things that you would never have suspected if you had not read the implementation.
(If you own code is in JS, that is)
And given enough public modules, we will find some conflicts in the dependency graph. YOLO just use the latest version is rarely a full solution.
Not… whatever this is.
I shouldn't have to say this, but at least 27 people upvoted this article, so here we are.
isolatedDeclarations is most useful for large codebases and other situations where performing a full Typescript compilation is prohibitively expensive. This is not the vast majority of cases. It is the declarations analog of transpileOnly, which skips all type checks and simply strips Typescript's special syntax.
The author seems to assume use of JSR, which is an alternative to the NPM registry which automatically compiles Typescript. Not being terribly familiar with it, it's possible this is more relevant to users of that registry, but it's not clear why this is an issue when the registry itself is handling transpiling and declaration generation.
EDIT: To be clear, I think it's totally fine to publish your TS source in addition to your JS and d.ts outputs, just don't publish TS only packages, please.
I mainly work with Deno which can run TS files natively (disclaimer: I'm employed by them). This changes the parameters a little as all registries for Deno could always ships the TS sources directly. But when publishing to npm you always have to publish the JS sources to work with runtimes like Node that don't support running TS natively.
Your plea would be better if you mentioned what the actual reasons are.
This is all a massive breach of encapsulation.
Is it possible to consume a TS only library distributed via NPM in a Node.js Typescript project? Yes, certainly. Is it ergonomic for consumers? No, certainly not.
Overall, it will be a bad experience for a lot of environments where JS files are expected to be present but aren't. Deno is probably the only environment that can consume NPM packages where it could conceivably work.
While it would be nice to have native support for package components, personally I think the best path is to ship all of it (save perhaps documentation, where there's currently no ergonomic way for a consumer to make use of it) in the main package.
The above is assuming "docs" mean some kind of external documentation, you absolutely should not strip out your docblocks when you package your code. Ditto for minification: Just don't. If the end user needs to optimize for size, they will be minifying anyway, all packaging minification does is create painful debugging and code reading experiences for consumers.
Is the entire article based on the assumption that everyone has Typescript installed in every project? Otherwise, how would publish Typescript files work at all?
> We only ever ship build artifacts, the compiled JS and the relevant .d.ts files in a package.
Isn't this because anyone, regardless of whether they have use the project in JS or TS, can import the library without caring out how the library code is implemented (in JS or TS)? It is not perfect but has worked for almost everyone. How would the author's project layout help people who only ever write code in JavaScript and never installed the Typescript package? Does Typescript have to be a dev dependency of any project that has an upstream package written in Typescript? That seems a bad idea.
I write almost all my JavaScript projects with Typescript now (it's a bad sentence but you know what I mean), but I don't think we should ever make the life harder for those who only write in JavaScript.
I'll try my best to break it down succinctly:
1) Historically, .d.ts generation is slow because it requires the full TypeScript type checker in order to correctly handle all cases (e.g. exported functions with inferred return types)
2) However, if types were to be fully explicit, then .d.ts generation could be performed without a full type checker (i.e. by a faster compiler via mere syntax transformation)
3) The isolatedDeclarations flag causes the TS compiler to trigger errors when any exports would require inference in order to generate .d.ts
4) Thus, the isolatedDeclarations flag can be used to guarantee compatibility with faster, simpler .d.ts generation using syntax-based transformation
5) A consequence of simpler .d.ts generation is that it can be trivially done in parallel, which can be helpful for large monorepos
You're spot on with all of your points. The isolated declarations feature forces your code to be written in a way that creating the .d.ts files is a mere exercise of stripping syntax and can be done easily without invoking the tsc compiler.
For runtimes like Deno which support running TS natively (disclaimer: I work for Deno) you never had to care about creating .d.ts files when publishing your package to any of the Deno registries: previously /x, now JSR. In the background though we've always tried to feed the tsc compiler in Deno something like .d.ts files though as it's quicker for type checking purposes.
It really doesn't do the things the author thinks it does.