Skip to content

Comment on Insights from Adopting TypeScript at Scale

Comments

"9. Generated declarations can inline types from dependencies"

Why do they generate .d.ts files instead of publishing .ts files alongside .js files? This should solve inlining issue, no?

That's an excellent suggestion!

It's true you could just have every package publish the raw TypeScript source-code. So that when an app imports a library, it type-checks against the original source code of the library. No need for DTS files from your dependencies!

I have never seen this done in practice for a large system. It's not as scalable as using bare-minimum type declarations that you find in DTS files. It requires more parsing, and potentially more time to accumulate the types. Whereas DTS emit in TypeScript can flatten the resulting type into the DTS file.

But if performance didn't matter, I think this would probably work. And it would eliminate the class of bugs where the generated DTS files are not 100% semantically identical to the original source code. That's another edge-case finding I omitted from the document but hope to write up another day.

It works quite well, you can forget about src/ lib/ directories, just structure package as you want (ie files at the root), generate js files along ts in the same directory (relative paths to assets stay the same, import paths are the same in js/ts, very useful), add in vscode rule to auto hide .js file if .ts with the same name exists `{"files.exclude":{"/*.js":{"when":"$(basename).ts"}}}` - coding is pleasure, you just see and work with ts, no need to generate js files most of the time (on prepublish yes) etc.

Given Bloomberg already moved most of their infrastructure to TS projects and incremental compiles already, so performance might not even matter or change substantially if they moved to TS only builds so long as they preserved project boundaries. Under the hood the TS compiler in incremental multi-project builds still essentially builds the equivalent of minimal DTS information for projects, but it caches it in a build file that's more compiler-internal oriented than the externally shareable DTS.

A developer with clean/fresh clones would still notice a big performance difference, but amortized over a number of builds it might not matter in general practice. In theory at least, as Bloomberg notes projects and incremental builds are still newish and don't yet always have the performance characteristics they should have in the wild. That said, the impression is that a lot driving projects/incremental compiles is monorepos in Microsoft (at the very least) that have moved to/are moving to monorepos that are entirely TS only builds with few/no DTS intermediaries. (That's just me reading between the lines, of course, I could be mistaken.) The drive behind projects/incremental compiles sounds like it is attempts at massive scales of TS files.

Another consideration is that it only works if all the code agrees on tsconfig settings (e.g. strictness settings and path resolution). That can work in a monorepo but won't work if you mix libraries.

Yes, they mention taking over tsconfig management because even generated .d.ts files are sensitive to tsconfig - article mentions type being different depending on the tsconfig setting.

But in general publishing the most strict ts should work fine when using from weaker tsconfig.

Personally I don't use custom path resolutions, can't comment on that.

ps. oh wait, but why does it matter? .d.ts file won't be different if you change strictness in tsconfig - why would it make difference for published .ts files?

Suppose project B uses lax tsconfig settings. Now suppose strict project A imports from B. If A uses B via d.ts it may be ignorant of most of B's choices. But if A uses B as source, A's strictness settings will now attempt to apply to B.

But you can use compiler option skipLibCheck, right?

AboutSource Built by g1lg1l

Hackerly is an independent reader for Hacker News, built on the public HN API. Not affiliated with Y Combinator.