Skip to content

Fast Typescript Compilation


Published At: Tuesday, August 27, 2024 at 11:47 AM

Typescript Compiler tsc is complex and a really powerful tool. Typescript type-system is turing complete and hence just the type system is akin to a programming language. This is great in general however sometimes you don’t need all that powerful type-checking. One use-case can be that if the types were checked in CI step, then the deployment can just worry about transpilation, another could be that you want to use a faster tool (esbuild/swc) for transpilation and just use the type checker from tsc.

All this is enabled due to tsc being essentially comprised of two parts → typechecker + transpiler.

The typechecker needs to do multiple pass because of the complexity of typescript’s type system and it creates an Abstract Syntax Tree then it runs the checker which is just a single extremely large file: https://github.com/microsoft/TypeScript/blob/main/src/compiler/checker.ts which returns the error/warning as the output.

The 2nd part is the transpilation which essentially doesn’t needs a lot of help from the type system and AST. There are exceptions in cases like Enums which are only a TS feature and not JS, but other than such specific cases, transpilation to JS is a far simpler endeavor. It mostly involves erasing all the types from the code and producing the JS code (_.ts → _.js). There is an interesting ongoing proposal to have this kind of erasure as part of native Javascript, https://github.com/tc39/proposal-type-annotations which would allow even direct TS code being executed without external transpilation.

Typescript compiler also has a new flag for tsc which disables checker and only does transpilation: https://devblogs.microsoft.com/typescript/announcing-typescript-5-6-beta/#the---nocheck-option, and there are flags --noEmit & --emitDeclarationsonly which skip transpilation and only run checker. However all this still has some disadvantages compared to 3rd party tools like esbuild and swc.

Esbuild: https://esbuild.github.io/

Rust: https://swc.rs/

Advantages of esbuild or swc for transpilation

--noCheck flag for tsc still is still beta as of this time

esbuild is in Go and swc is written in Rust, compared to tsc being in javascript. They are faster languages due to javascript being dynamically typed. swc is even faster than esbuild due to rust not having a GC and other reasons.

They make much better use of parallelism for multi-core processors. tsc runs mainly runs in single threaded environment.

Faster test execution with @swc/jest vs ts-jest

Another place where transpilation is needed is for Jest tests, which use ts-jest which uses tsc under the hood to transpile ts code, in order to run the jest tests over them. Since all the same disadvantages of tsc apply for this use-case, there is an alternative by swc project @swc/jest which allows our tests to run much faster. There are a handle of cases where @swc/jest is unable to transform the code correctly, but for the vast majority of cases there is no down-side.

Building with swc vs esbuild vs tsc

"build:swc": "swc . -d dist --copy-files --ignore dist,node_modules",

"build:ts": "tsc",

"build:esbuild": "esbuild src/main.ts --bundle --outfile=dist/main.js --platform=node",

"build:swclean": "swc src -d dist --copy-files",

Using @swc/jest

hyperfine params - 10 runs 3 warmup runs

A. with @swc/jest

B. with ts-jest

Building for upload-offline-conversion service with swc vs tsc

Caster Tests ts-jest vs swc

ts-jest:

@swc/jest:

Build-Time: tsc vs tsc --noEmit

tsc --noEmit just does type checking and is faster than running the full tsc command.

Rush Coverage (over all backend) on Github CI (@swc/jest vs ts-jest)

@swc/jest run #1

@swc/jest run #2

ts-jest run #1

ts-jest run #2