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
files.
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:
import {} from "rxjs";
import {} from "rxjs/ajax";
import {} from "rxjs/fetch";
import {} from "rxjs/operators";
import {} from "rxjs/testing";
import {} from "rxjs/webSocket";
.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 the best case check penalty.
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.
- typescript 5.2.2
- Node.js 14.21.3 (working on something old recently)
- tsconfig:
noEmit
,"module": "nodenext"
- test file:
index.ts
(CommonJS format) - Apple M2, 16 GB RAM
- stats from
tsc --extendedDiagnostics
- times average of three runs
Modifications to node_modules/rxjs/package.json
took the form
"exports": {
".": {
- "types": "./dist/types/index.d.ts",
+ "types": "./src/index.ts",
"node": "./dist/cjs/index.js",
"require": "./dist/cjs/index.js",
"es2015": "./dist/esm/index.js",
"default": "./dist/esm5/index.js"
},
for each "exports"
subpath (except "./internal/*"
which was not directly imported from the test file).
Yes, performance isn’t the only potential problem.