Hacker News new | past | comments | ask | show | jobs | submit login
I'm making a Typescript type-checker in Rust (zackoverflow.dev)
153 points by bpierre 5 months ago | hide | past | favorite | 48 comments



Typescript is a great project but I feel it’s fast outgrowing the JavaScript it’s written in, similar to when Sass outgrew Ruby and needed to be rewritten in C.

Projects are getting larger and more complex and tsc is often becoming the bottleneck performance wise: usually waiting for typechecking to finish, having your fans spinning in the background, type-stripping during development then type checking on CI. The performance needs from users seem to be outpacing what tsc can provide.

Something has to give, it would have been nice to see Microsoft create an official Rust implementation but i feel we’ll get one whether it’s from them or not.


> Sass outgrew Ruby and needed to be rewritten in C

libsass (the c port) has been deprecated for a while because it couldn’t keep up featurewise. The reference compiler is now written in Dart.


> when Sass outgrew Ruby and needed to be rewritten in C

And we’re all so happy that every time we install our node project we get a whole C build toolchain as part of the requirements…


You’re right it was annoying, but there’s ways around that today.

One is the ESBuild path where they bundle the pre-built binaries for each platform so you don’t need to do any building/node-gyp on your machine (only the binary for your platform is downloaded, and because it’s via npm it shouldn’t hit any proxy issues, unlike gyp’s postInstall mess). The other is to compile down to WASM as the sibling mentioned.


With WASM this should not happen anymore. The tools are compiled once to WASM and then ran on every platform that Node works on.


I think it only has to compile the dependency if it isn't cached for the given platform and version(s).


That’s true, but since I’m annoyed by it I’m inclined to say that happens often enough ;)


Whereas before you needed the correct Ruby runtime installed.

I'm primarily Ruby developer, so I'm biased. But getting the right version of a Ruby runtime, then get the correct gems is a mess. Certainly not easier than getting some libc packages.


A TS type checker in Rust the author started working on a week ago and hasn’t released in any form.

Would be more exciting when there’s something to try out.


I'd agree if this was shared by the OP of the article but it wasn't


Seems like a project made for HN posts.


Sounds like a comment from someone who spends too much time on HN. The project author is not OP, and have you never heard of people taking their time to release personal code?


That's not how HN works. Readers here are accustomed to being able to try out projects, browse code, or get some sort of significant window into a project or product.

(Incidentally, would you mind reviewing the site guidelines at https://news.ycombinator.com/newsguidelines.html? You broke them with this comment.)


> Additionally, I want to tyty to have better and more descriptive error messages than tsc's. Isn't this nice?: (https://zackoverflow.dev/tyty-errors.jpeg)

Actually, no. It looks fancy, sure, but error messages aren't there to be pretty, but to be usable.

1. I expect the first line of an error to give me a succinct, yet precise description of the problem.

     Error: Incompatible types
It would never be possible for me to fix code based on this alone. Contrast with Python:

    TypeError: unsupported operand type(s) for +: 'int' and 'str'.
I'm sure you can easily figure out what code triggered the above error just based on itself.

At minimum, the first line should say which types are incompatible:

    Error: Incompatible types MyType and MyType2
2. How am I supposed to read the

    Because of this type annotation
part? Is it

    MyFn has the type: X
    Because of this type annotation
or

    Because of this type annotation
    MyFn has the type: X
?

To fix, include guiding punctuation:

    MyFn has the type: X ...
    ... Because of this type annotation
or

    Because of this type annotation ... 
    ... MyFn has the type: X.
Actually, it looks like it's neither and I'm somehow supposed to read it as

    Error: Incompatible types, because of this type annotation
... which is impossible to figure out without context. I should be able to read the error message top-down or bottom-up.

To sum up, error messages are there for the programmer to be able to quickly get the gist of what happened, so they should be as obvious as possible. Check out Rust's errors for some inspiration: https://blog.rust-lang.org/2016/08/10/Shape-of-errors-to-com...


I agree that having a good summary on the first line is important, and that the actual wording of the messages shown is a little confusing, but:

- The annotations shown look like a prettier version of the ones done by the Closure Compiler, which I find very helpful. - Some types can be really long, so putting two of them on the first line may not always be as readable as the TypeError example you gave. Putting them one above the other can make it much easier to spot the difference.


Yes, and the fanciness part is done by another crate actually: https://github.com/zesterer/ariadne.


I think as long as you can set the view mode with a flag to the CLI it is a great addition to the development process


Best of luck to them. TypeScript is one of the most sprawlingly-complex languages/type systems around, so this is super ambitious! But that's also what would make it valuable


Well I’m excited about this. And if the author wants another set of eyes, before it’s ready for public consumption, I would love to be one of them. My main weekend project has been oriented around leveraging the emit side of faster compilers, but a faster type checker has a lot of advantages for other use cases I’d like to explore also.


I'm glad people are working on this problem. Typescript's compiler performance is certainly a weakness.

Other commenters have rightly called out the complexity of the type system as the reason why this is difficult. The other reason is that Typescript does not have a formal specification. The tsc compiler is the only reference for the correct behaviour. It would be great if the official compiler and these reimplementations could share a common test suite to ensure compatibility.


I suppose picking top X open source Typescript codebases and compiling them with the expectation of output being identical with TSC would be one way to test this, albeit not what one would expect as a test suite.


they can. thats exactly what the swc guy is doing. reusing the tsc tests


This is sorely needed. Goes without saying that it's a ton of work because Typescript is really complicated, but if he pulls it off, or gets enough momentum to pull in people who can help pull it off, a lot of people would be really happy.


Ironic. Rust is saving others from slow compilation speed, but can't save itself.


> but can't save itself.

Sort of. The Rust compiler has gotten much faster in the last few years. On most common crates it's become 25-30% faster (https://arewefastyet.pages.dev). This page isn't up to date with the latest releases, which have also become faster.

During development the most typical configuration would be debug-incremental builds. Those are pretty damn fast. And personally, I don't even use rustc during development to figure out errors. rust-analyzer does a great job of giving all errors in line. I think most others who use one of the IDE options might not be waiting on rustc much.

Honestly, "rust is slow" is a bit of a meme at this point. There are engineers working on triaging compiler performance regressions every week, as well as engineers working on improving performance. But there's no concerted effort to communicate these efforts to the community. Which leads to this meme being perpetuated. I guarantee that even if the compiler improves by a further 50%, there will still be comments saying it's too slow, simply because commenters aren't aware of changes over time.


> Honestly, "rust is slow" is a bit of a meme at this point.

But it _is_ slow. Same as C++ and Haskell compilers are slow. And you cannot get it to really compile faster - like OCaml speed - because the language (macros and traits I guess are the worst) prohibits that. Of course, if all somebody knows are C++, Haskell and Rust the compilation doesn't seem slow to them.


> (macros and traits I guess are the worst)

This is a common assumption but I've rarely see cargo check, which both expands macros and solves traits as well as runs the borrow checker take more than a few seconds on my machine, even when full compilation takes minutes. Most of the slowness is really in the parts that come afterwards, I suspect especially once you hit the generics explosion in later passes.


Ironically C++ can be faster than Rust in practice, because the ecosystem embraces binary libraries, so unless one is doing crazy stuff with Boost, C++ manages to have clean builds that are faster than Rust's clean builds.


Each crate is a separate build unit, so after the first build your dependencies won’t recompile unless you change the optimization-level or something


You know what is better? Not having the first build at all.

Microsoft packages a full set of binary libraries with a build.rs script for Rust/WinRT, otherwise no one would bother to wait for the build.

https://github.com/microsoft/windows-rs/tree/master/.windows

Also seeing the same crate consumed in multiple version being compiled multiple times isn't fun.

For example, the image crate.


I was under the impression that must of Rust's compile time comes down to LLVM


It's less LLVM itself and more the amount of work that rustc gives to LLVM. Formerly for example, rustc did not do any kind of constant propagation itself and relied fully on LLVM to propagate them and say, remove any unused branches in the final expanded code. This wasted a lot of work and propagating them earlier was able to reduce compile times significantly.

So while most of the compile time is indeed usually in LLVM, there are still a lot of things rustc can do to give LLVM an easier time.


I guess using LLVM doesn't help performance, but the real 'problem' still is the language itself. Just look at C++ and it's compilers for comparison.


> MyFun has the type

I hope you fix this, because it is wrong. MyFun is the type. woops has the type because of the annotation.


Similar work is also taking place in swc:

https://github.com/swc-project/swc/issues/571


Unfortunately, this will not be open source:

https://github.com/swc-project/swc/issues/571#issuecomment-7...


The article mentions various projects parsing TS, e.g. esbuild and TSC. Are there any in the JVM ecosystem? I know Google Closure does, but the code is pretty inscrutable. I've also tried using the ANTLR4 TS grammar, but it seems pretty out of date. In particular, I'm interested in parsing TS type declaration files, so just a TS subset.


Desperately needed!

I hope more contributors support this side project and push it to a non side project.

Use a dual license, open source and free use for everybody and commercial for companies (e.g. with minimum revnue of 1 million dollar).

I am sure this project could get easy sponsors, so no commercial license would be necessary.


p.s. i would spent some money, cause it would save me a lot of dev time.


Could tyty emit declaration (.d.ts) files fast while it type checks?

I'm pretty sure I'm not the only esbuild user frustrated with having to `tsc --emitOnlyDeclaration`.


> Amazing projects like esbuild, SWC, and bun solve the first half of the problem, emitting JS from TS, but they skip the critical type-checking phase completely due to complexity.

I love the idea behind it, but I'm also wary of now potentially having yet another tool that only solves "half of the problem". Could this potentially be contributed/merged with SWC in the future, seeing as it's also Rust-based?


Not if SWC wants to build it’s own proprietary version.


I’m glad someone is working on this. TS is fantastic but the build and typecheck times (both for run and test) are awful.


I would love to have some compile time constant resolving in typescript. I’ve got a project that generates an array based on some smaller input and it would be fantastic to not have to do that every time the script is loaded.

Really exciting to see people exploring new and other idea of where to take typescript.


Sounds like you're looking for partial evaluation for JS. From a quick search, prepack.io might suit your needs?


Would also be nice to create a TypeScript variant with a sound type system, and this might be a better project for creating it than tsc.


Source or it didn’t happen! ;)


The error messages are neat!




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: