# Status update of my tsc port


I'm the creator of [swc][], a web build tool written in Rust.
And I'm working at Vercel to make web development as fast as it can be.
My go-to language is Rust, but I selected Go for the new TypeScript type checker.

- Related: [I'm porting tsc to Go](/posts/2022/1/tsc-go)

## Porting TypeScript compiler is hard

Every web developer wants a faster TypeScript type checker.
But there's only one implementation of it because implementing it is way too hard.
TypeScript does not have specification and `tsc` is the only thing which can be called a _reference_.

But well... `tsc` is a moving target, and the velocity is far from common sense.
Definitely, there's no other word which can represent it correctly.

Let's compare `v4.7.2` and `v4.8.2`.
These versions are the first official stable releases of `4.7` and `4.8` respectively.

- [Github link](https://github.com/microsoft/TypeScript/compare/v4.7.2...v4.8.2)

There are 311 commits and 569635 lines of codes are changed.
Yes, that’s correct.
311 commits. In one minor version bump.
Following `tsc` with swiftness, and with custom reimplementation is nearly impossible.

That's why I decided to port `tsc` to Go, not Rust.
As TypeScript and Go shares lots of common properties, I can rewrite almost all TypeScript code in Go line-by-line.
But while trying to do it, I found a better methodology.

## My methodology

Porting line-by-line is easy.
But... there are so many *line*s.

```

TypeScript on  release-4.9 is 📦 v4.9.1-beta via  v16.17.0 took 2s
❯ tokei src
===============================================================================
 Language            Files        Lines         Code     Comments       Blanks
===============================================================================
 JSON                   32         9076         9032            0           44
 Markdown                2           59            0           37           22
 TypeScript            538       346496       270968        42109        33419
===============================================================================
 Total                 572       355631       280000        42146        33485
===============================================================================
```

The `src` directory of TypeScript contains 346496 lines of TypeScript.
Line-by-line porting is easy, but 346496 \* (line-by-line porting) is definitely not easy nor funny.

No one, at least on this planet, wants to port hundreds of thousands of lines of code on one's own.
As I'm one of the people living on this planet, I looked for another way.
It should be much less cumbersome way than line-by-line port.

**Then I got an idea.**

If we want a line-by-line port, computers can help.
All about computers are automation.
Why can't we apply it here?

We can write a compiler which takes TypeScript compiler as an input, and emits a new TypeScript compiler as an output.
Of course, output should be written in Go.

Yeah, this is _insane_.
This is way more insane than velocity of `tsc`.
But one thing we should note is that following `tsc` becomes viable if we transpile `tsc` to Go.

I'm an experienced compiler developer.
I have been maintaining [swc][] for several years.
So I didn't need to study about compiler internals, and I just started writing a compiler.

## `ts2go`: The transpiler

TypeScript contains a file named `checker.ts`, which is super big.

- [GitHub link](https://github.com/microsoft/TypeScript/blob/a7d10f15bbd28166b869ae00482214e360891613/src/compiler/checker.ts)

Guessing from the name of the file, I expected this to contain core validation logic.
I chose TypeScript for the language of TypeScript transpiler, because I can access type information while transpiling.

Of course, emitting a Go file which can be consumed by `go build` was a hard task.
But it was something doable.
I simply invested lots of time.
After fixing all syntax errors, `checker.gen.go` had **100K+** compile errors.

## `tsfix`: Go autofix tool

I found that what I can do from TypeScript compiler is quite limited, because I can't access any information from custom Go code.
So I investigated.
While searching, I found that `golang/x/tools/analysis` supports `--fix`, and it supports using the type information..
Lint tool with autofix can be used to modify code automatically.
So it was almost a perfect fit.
Once I implemented it, it worked!
I had to deal with some bugs, but it was fine.

It perform various operations.
It modifies type definitions, automatically convert types, and perform much more, based on the CLI option.

## Status

While working on it, I found that it would be much better if I generate more files.
So I did so.
Also, I improved `ts2go` and `tsfix` over time, and I reduced the number of compile errors.

```
    1298
Per file:
  32 ts/internal/checker/binder.gen.go
 713 ts/internal/checker/checker.gen.go
   2 ts/internal/checker/checker_base.go
  17 ts/internal/checker/core.go
   1 ts/internal/checker/factory_node_factory.go
  31 ts/internal/checker/factory_utilities.gen.go
 117 ts/internal/checker/moduleNameResolver.gen.go
  79 ts/internal/checker/parser.gen.go
   6 ts/internal/checker/path.gen.go
  86 ts/internal/checker/program.gen.go
  23 ts/internal/checker/scanner.gen.go
 181 ts/internal/checker/utilities.gen.go
   9 ts/internal/checker/utilitiesPublic.gen.go
   1 ts/internal/checker/visitor_public.go
```

From **100K+** compile errors, to **1298** compile errors.
Still, there are lots of tasks to do.
But once the number become small enough, I can switch to manual implementation, which is quite simple and fast.
It requires applying same manual patch for each update but it will allow faster release and I'll improve automatic conversion rate over time.

## Moving forward

For now, my focus continues to be [swc][], in particular making its minifier production-ready and ensuring a successful integration into Next.js
And because this is still my private project, so I can't use working hours for this.
Thus, I can't guarantee when this will be ready.

---

[swc]: https://swc.rs

