Published: September 23, 2023
11
19
182

What if TypeScript libraries published just .ts sources to npm instead of .js and .d.ts files? This might already be tempting for Bun-only libraries, but how will that impact users? This is easy to answer by experimenting on existing libraries that ship .js, .d.ts, *and* .ts. 🧵

RxJS ships .js and .d.ts files, but also .ts files for debugability purposes. By tweaking its package.json "exports", we can compare tsc performance on this file with imports resolving to .d.ts files vs .ts source files.

Image in tweet by Andrew Branch

.d.ts / .ts Files: 268 / 317 LOC: 49k / 62k Instantiations: 11k / 46k ❗ Memory: 92 kB / 118 kB Parse time: .76 s / .91 s Check time: 1.48 s / 3.02 s ❗ Total time: 2.58 s / 4.43 s ❗

Obviously .ts files look really bad here, which is what I was expecting. I did expect the memory/parse difference to be more dramatic, and the check difference to be less, though! RxJS already uses isolatedDeclarations-compliant style, so this is likely *best case* check penalty.

Takeaway: do not skip .d.ts emit if you care about your users’ DX, even in a world where all your users can consume TS directly.

Test details: - typescript 5.2.2 - Node.js 14.21.3 (working on something old recently) - tsconfig: noEmit, module nodenext - Apple M2, 16 GB RAM - stats from tsc --extendedDiagnostics - times average of three runs

@atcb Thought experiment. Type annotations lands in JavaScript. You can add types to .js files. Much rejoicing. You're in a monorepo, in a setup where only your team is consuming the packages you create. It sounds like you'd still recommend running `tsc` on those source files to

@mattpocockuk Short answer: yes (or at least make .d.ts files). I think in a future where most packages ship typed implementation files (be it .js or .ts) it’s worth thinking about tsc/tsserver optimizations that could make this .ts/.d.ts perf gulf smaller... 1/2

@atcb Very cool thread, instant bookmark

@mattpocockuk I’ve been griping about .d.ts importance enough lately that I figured it was time to back it up with data 😄

@atcb I hope they pay you extra for working on a weekend. This thread is gold.

@robpalmer2 Hey, you make a pretty good point. @satyanadella whaddya think

@atcb Bun already resolves ts *before* js in node_modules if published along side the exported js files – which was surprising and resulted in transpiler errors in some of the libraries I tested.

@dpeek_ I think most TS bundlers do this too, so I guess it’s not surprising, but it is a bit of a weird choice IMO.

@atcb Speaking of performance, do you know if there's much of a perf penalty to specifying `node_modules` in `typeRoots`? I found out that I need to do that in order to get vite's types. #issuecomment-1589593147 class="text-blue-500 hover:underline" target="_blank" rel="noopener noreferrer">https://github.com/microsoft/T...

@IanVanSchooten does "types": ["vite"] not work?

@atcb Wouldn't that limit libraries to avoid any new TS features, because the library couldn't be bundled with an app or bundler using an earlier version of TS?

@justingrantjg Yeah. Though this can be a problem for .d.ts files too

@atcb I also wonder the diff between bundled .js and .d.ts vs individual files

@atcb That's what I already do for all my INTERNAL packages. Required minimal configuration in my tooling to get it working.

@atcb Legit only if it uses condition

@atcb Deno uses the 'deno' target in the 'exports' directive of package.json when importing npm modules. As the package author, you can specify the .ts file in there. I'm working on a lib that exports 'browser', 'node', 'types' and 'deno'. IDK if Bun has its own target 🤔

Share this thread

Read on Twitter

View original thread

Navigate thread

1/22