Ben's Engineering Blog

The quest continues

It has been nearly a year since the last post so thought it would be good to do a much-needed status of the project! While not production-ready yet there has been some great recent progress in the last year. The project has passed 2000 stars, 10 external contributions and it now passes over 200 checker tests.

If you haven't heard of it yet, Ezno is a new TypeScript type checker that experiments with deeper analysis to raise new errors and find new optimisation opportunities!

This post will go over more of the state of the compiler and tooling. While there has been some huge technical progress on the checking and compilation side, I will leave it out of this post and split the technical details across future blog posts!

There is a new web-based playground

playground

This can be used to try the checker. You can see some of my favourite features here

  1. Triangle number constant calculation
  2. Tracking a value across a closure
  3. Object.entries

Note the playground is WIP. Identifiers for sharing currently use a sequential index, which may change in the future.

You can also try the checker locally with the CLI from the latest GH release

The test suite

Since last year, I have started on a new collection of type-checking tests for the checker. In its latest release, the checker passes 235 tests!

235 passing tests

There have been a lot of improvements in TypeScript features: complex subtyping, mapped types and extends annotations. There is now a good foundation for the checker but there is a still a lot to do. There are some general JavaScript and TypeScript features to finish:

  • Inference (was working, now being redone to work around Rust's mutability)
  • Narrowing (got some parts working, working on getting more edge cases narrowed)
  • More collection logic (Array, Map, Set etc) (array methods have been added, but are yet to work perfectly)
  • Generators (require storing and partially evaluating events)
  • For-of (should work by de-sugaring iterator calls, but a bit complex)
  • Recursion
  • Functioning queuing/non-synchronous calls and promises (requires more unknown-ness and coordination between events)
  • Improving how types are stored (current implementation doesn't scale for larger projects), which should then allow for more of the standard library to be used
  • node_modules imports (files work, zipping source up with definition files is left to do)

and there is a general need to improve all the different edge cases in which break the checker.

There is no time frame or deadline for these. But as long as I can continue with the current trajectory they will eventually be all good!

At the moment there are 235 passing tests. The hope is that the checker should be ready for simple projects at around 300 passing tests and larger projects at around 500 passing tests. You can see a collection of things that do not work in to_implement.md.

I am looking for more contributors to the checker tests. If you have a favourite TypeScript or JavaScript feature not included in the current specification (or to_implement.md) it would be really helpful to contribute it

These tests are authored as markdown and automatically built into tests that Rust can run. You can find out how a markdown file works as a test suite here.

New additions from contributors

Thanks to Charles Taylor for adding default value checking and checking for the type of catch variables.

default-value-and-catch-check

Thanks to Lemuel DLS for checking the edge case for destructuring with this

this-unbound

Thanks to Patrick Laflamme for adding excess property checking (including across conditionals which TS does not check currently as was pointed out in a this issue)

excess-property

and again for setters

setters

Thanks to Apika Luca for adding import.meta

import-meta

Additionally, there have been improvements internally to the project. Wan added positions to events which have enabled emitting some new errors (that will be featured in the future). Jules Guesnon added export type parsing and improved the clippy configuration, which has helped keep things in order as the project grows. There are now fuzzing tests for the parser thanks to Caleb Jasik and Addison Crump, which has caught some problems in parser and to string-ing (turning the AST back to a string). Nathan added the parsing for try-catch-finally. markthree added CJS support for the JS library and Nicholas Roberts added type definition generation for the JS library (the type definitions have been very useful when building the playground). You can see these contributions and more here.

Despite Rust being a non-trivial language to author and the type checker being very complicated and entangled, I have been impressed at the contributions to the type checker and project!

Bad news

Unfortunately, the first half of the year has been a bad month for JavaScript type checkers. First, the STC (speedy type checker) was abandoned and now Hegel is over. As someone who knows the difficulties of building a type checker, I commend the incredible work put into the two. Hegel is especially impressive with its parameter constraint inference, which is an incredible feature I am hoping to implement as well in the checker.

I am hopeful that Ezno's wider feature scope compared to STC is more of a safe option (Ezno is not exclusively focused on checking performance). While I thought Hegel made some great trade-offs to provide better checking it seemed maybe too much for potential users compared to TSC. I will explain more of how I think you can add better checking behavior while not breaking existing valid cases allowed by TSC in future blog posts!

Also, the ezno-checker is no longer included under Oxc and the only current way to use Ezno is through its own toolchain. Oxc is an impressive project for performance but differences in timelines and targets meant that it didn't make sense to put the work in maintaining the AST bindings. That is just for the moment though, the checker crate still keeps the checking logic separate from the AST/syntax implementation. Hopefully in the future when more of the language is implemented other tooling can consider reusing or copying parts!

Supporting the project

I have written some good first issues in the issue tracker. If there is something you want to contribute then I can help explain certain nuances in the checker and give steps to start! You also don't need to be good at Rust to contribute, adding more checking tests is useful for knowing how to approach new features! Type checkers are hard and Rust is not an easy language to write in. However, at this stage, you can have a really big impact by adding fundamental features! I appreciate all pull requests no matter how ready they are (I would rather have some code that doesn't quite work than none at all).

I have GitHub sponsors, which very generous people have signed up for! I have uploaded the LSP (a plugin for doing type checking in editors like vscode) to a sponsors-only repository and plan to also commit other experiments and early blog posts there. That is available at any level of sponsorship and I also plan to add code contributors to the main repository there as well!

Unfortunately, a lot has come up in the last year which is why it has been a while since the last update. Again, a lot of progress has been made internally so I hopefully can share some amazing progress on type-checking and compilation soon!