Description
Suggestion
TypeScript supports relying on type inference to produce (parts of) the API of a module. E.g. users can write (sorry, slightly contrived):
// in counter.ts:
import {Splitter} from 'textutils/splitter';
export function countParts(x: string) {
return new Splitter(x).splitWords().size();
}
Note how you need to specify the type of x
(otherwise it degenerates to any
), but can leave out the return type of lengthOf
if you like. TypeScript will infer the type, potentially using type information from the file's (transitive) dependencies.
This causes two problems.
Readability. It is difficult to understand what the return type of countParts
will be. This is purely a stylistic issue that could be fixed with a lint check (though there is some complexity e.g. due to typeof
).
Compilation performance/parallelism.
Imagine you're using project references, and you have a dependency structure:
app <-- counter <-- textutils/splitter
To compile, we need to first compile textutils/splitter
, wait for that to complete, e.g. 5s, then compile counter
, wait e.g. 3s, then compile app
(6s). Total compilation wall time is |app| + |counter| + |textutils/splitter|
, in our example 5s + 3s + 6s = 16 seconds
.
Now assume we could produce .d.ts
files without requiring transitive inputs. That'd mean we could, in parallel, produce the .d.ts
files for textutils/splitter
, counter
(and app
, though we don't need that). After that, we could, in parallel, type check and compile textutils/splitter
, counter
, and app
. Assuming sufficient available parallelism (which seems reasonable, given how common multicore CPUs are), total compilation wall time is the maximum time to extract .d.ts
files, plus the time for the slowest compile. Assuming .d.ts
extraction is purely syntactical, i.e. does not need type checking nor symbol resolution, it shouldn't add more overhead than a few hundred ms. Under these assumptions, the wall time to wait for the project to compile would be 500 ms + 6s = 6.5 seconds
, i.e. more than a x2 speedup.
The problem with that is that we cannot produce .d.ts
files without running full type checking, I believe purely due to type inference.
RFC thus: I wonder if this would sufficiently motivate the ability to restrict using type inference in exported API?
E.g. we could have a noTypeInferenceOnExports
compiler flag, that would allow TypeScript to parallelise emitting .d.ts
across project references, and then parallelize type checking.
The counter point is that projects that experience slow builds in edit refresh situations might instead want to turn off type checking entirely for their emit, at least on critical paths. However that means users do not see compilation results, and produces additional complexity (e.g. when and how to report type checking failures).
Impact
We've run some statistics internally at Google on build operations.
As one would expect, this change has little impact on most incremental "hot inner loop" builds, as those typically just re-type check a single file and produce no .d.ts
change at all, so caching saves us from long build chains. We're seeing ~20% wall time improvements in the 90th percentile across all builds involving TypeScript.
However the impact on slower builds is more substantial. For a sample "large" project that sees both slow individual compiles and a long dependency chain, we see ~50% improvement in the 90th percentile, and 75% in the 99th percentile (which is representative of CI style "cold" builds with little caching).
🔍 Search Terms
performance compilation parallelism inference declarations
✅ Viability Checklist
My suggestion meets these guidelines:
- This wouldn't be a breaking change in existing TypeScript/JavaScript code
- This wouldn't change the runtime behavior of existing JavaScript code
- This could be implemented without emitting different JS based on the types of the expressions
- This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.)
- This feature would agree with the rest of TypeScript's Design Goals.