Hacker News new | ask | show | jobs
by lewisl9029 1679 days ago
Decoupling build and typecheck is definitely a good first step, but poor typecheck performance is still going to eventually become a bottleneck in CI as project sizes increase.

At Brex we were running up against typecheck times of over 15 minutes at one point before we were forced to address it since it was frequently creeping onto the critical path. We ended up hacking together some CI scripts to share the incremental typecheck cache between builds, which made most checks reasonably fast again (usually well under a minute, averaging 10~ seconds), but there are still frequent large spikes (of 5-15 minutes) in typecheck times on tiny, seemingly innocent changesets (with few dependent files as far as we could tell) that can still really take a toll on productivity.

I believe the recommended solution to scale typecheck performance is to break down your app using interdependent project references that serve as atomic units that only get rechecked if they change, but I'm personally not a fan of this approach because most frontend projects don't really derive any intrinsic benefit from this style of organization (as they function perfectly fine as monoliths, and any added indirection only adds unnecessary friction), and project references themselves have a bunch of caveats and introduce a ton more tooling complexity: https://www.typescriptlang.org/docs/handbook/project-referen...

I wish instead of having to break down our apps into project references manually to reap the performance benefits, TypeScript could just treat each file as a "project" onto itself and give us those performance benefits automatically and by default. In the ideal world I should be able to just run `tsc --noEmit` with the list of files changed from git, and have typescript do the minimum possible amount of work to check all the potential files affected by the changed input files by walking up their dependency graphs, without having to structure my project in a certain way.

1 comments

Is what you’re asking for possible?

JavaScript can walk both directions on a dependency graph. You can import two things and inject the first dependency into the second.

So, unless you want to “structure your project a certain way,” you’re out of luck. If you ARE willing to conform to those patterns then you can get incremental builds.

As it happens, you can get something similar within a file by declaring a type interface, telling the compiler “you don’t need to walk the interior of this function because look see here’s the signature,” but you lose the power and dynamism of implicit types / complex generics by doing this.

I could be wrong about this; it’s been a minute since ts was my daily driver.