I've been working on a new TypeScript-like language myself from scratch, which among other things takes this approach. Mine can do some of the numeric stuff shown here, but I'm jealous of (inspired by? :)) some of the crazier stuff going on here, like usage-based argument types and tracing not just value shapes but value reference identities
The "automatic generics" feature in particular is absolutely bonkers. It never even occurred to me that you could do that. I'm wondering if there are unforeseen edge-cases, but also wishing I had it at work. Clunky generics syntax is one of the worst parts of TypeScript, even while generic function types are one of its best parts.
Wow, and side-effects-tracking too! Amazing
I am curious whether some of these checks will be limited by JavaScript's dynamism. Part of why I'm doing mine as a new language is that JavaScript's semantics are just way too flexible to form some of the guarantees I feel like you need for some of this aggressive inference. But now I'm questioning that.
Either way, this is insanely impressive. Definitely not just yet-another-JavaScript-toolchian.
The automatic / inferred generic restrictions is quite cool. https://hegel.js.org/ got there before me! Basic restriction modification is quite simple e.g. `(x) => Math.sin(x)`, x wants to be a number so can add that restriction. It gets more difficult with higher poly types. `(someObj) => Math.sin(someObj.prop1.prop2)` requires modifying not just `someObj` but a property on a property on it. And `(x, y) => printString(x + y)` requires doing even more complex things. But its definitely possible!
I've been searching for people who are doing this for inspiration and maybe exchanging ideas in chat. What I have seems very similar to Ezno when it comes to tracking mutations and side effects and the key idea of "maximum knowledge of a source"
I struggle a bit with communication about this topic as my way of learning has been more of a self taught trial and error process. It almost feels like the people who are building typesystems is speaking another language (especially those with a Haskell background), but I'm slowly starting to understand some of it. Your blog post however was easy to understand.
Inferring from usage is something I've been thinking about every now and then, and it seems doable if the type can mutate from some unknown type to the first expected type. In practice this doesn't seem to become a big deal if you always require input types to be typed (from arguments or some other external) but it's an interesting problem.
I think the way I would approach mutating someObj.prop1.prop2 is to do a mutation of the field when calling the function signature. I'd have to know a few things about prop2 though.
So it would effectively be something like Math.sin = (val) => {let [parent, key] = parentObject(val); parent[key] = number }
Then it would make use of the way i track mutations already.
My project's status is that it's not production ready but it's in the open. There's some use to the playground to analyze what Lua scripts might possibly do.
playground: https://capsadmin.github.io/NattLua/
source and further explanation: https://github.com/capsAdmin/nattlua
Not sure if I have any big plans, I just think it's fun and interesting to work on and maybe I want to use it for my other Lua projects. I'm not very happy with the codebase as I've written it in Lua. I'm trying to bootstrap it but it's growing in complexity faster than I can bootstrap it. But one step at a time and I should be there someday, maybe I can even port it to some other language like Rust when I have a better understanding of what I'm actually doing. :)
Blog: https://www.brandons.me/tags/bagel.html
HN (53 comments, 1 yr ago): https://news.ycombinator.com/item?id=28566597
I didn't want to do a shameless plug, but it doesn't count if someone else does it ;)
The issue is that when you have errors, it is impossible to determine if the error is at the caller, the callee or at the type signature. Essentially you have an equation of 3 dimensions with 3 unknowns. Same issue propagates into code navigation and refactoring.
It’s like regressing into a dynamically typed language except the errors are given during compile time. For small simple functions like the addOne example, it’s great to reduce boilerplate typing, but once you start relying on it for more complex types it can easily go out of hand.
Search for Hindley Milliner type system.
(This is something that's been possible for decades, but it never hit mainstream before as it's hard to implement it well enough to satisfy the silly preferences of us typical programmers :)).
The way sum types are done in TS is really awkward.
The type system is unsound.
What's worse is that you never have a guarantee that the types are actually correct at runtime, due to bad third party typings, compiler limitations, use of any, ...
It's still a lot better than using plain JS, and a lot of the limitations aren't by choice, but come from the need to compile down to and remain compatible with plain JS.
It just could be so much better.
It would hit the sweet spot for me, I know Rust is popular these days, but it seems like it's made for type astronauts, and sometimes I just want to write some code and get things done quickly and don't care about squeezing out every last drop of bare metal performance or abstracting seven layers of types to please a borrow checker.
let _: Result<Vec<_>, _> = some_iter.collect();
or like this: let _: Vec<Result<_, _>> = some_iter.collect();
The only thing that has changed here is the type annotation. Both will compile, but the implementation of `collect` is determined by that type. As far as I know this isn't the case in typescript, although please tell me if I'm wrong, I have limited experience from it and I'm thinking more from mypy perspective.This ends up meaning that my types don't drive the program as much. There's this sort of declarative "here's the thing I want, use an implementation of that method to give it to me" that feels very powerful. I miss this a lot with mypy, my type annotations feel very much like annotations, they are a separate part of my program.
If TS were to lean into itself as its own thing, not just a way to do better JS, I think that'd be amazing. I'd love to see an AOT compiled TS where types are a semantic part of the program, I'd love to see it drop `any` entirely as well, and drop support for using JS in TS.
As for soundness, don't care too much tbh.
I've never seen this as a barrier to producing working software.
Meanwhile Dart is sound, bit but doesn't have nearly the same adoption.
Where is this industry and how do I avoid it?
Static typing fell out of favor hard among certain crowds. Typescript brought many of them back to the world of static typing, and all three of the aforementioned languages are getting more static support, so it isn't nearly so prevalent as say 10 or 15 years ago.
I would disagree this is a TypeScript compiler. It's a mostly-typescript-compatible compiler/type-checker.
The Typescript team iterate on it reasonably quickly. If you fall behind as the Typescript team adds new features, and you cannot check that code, is it still "Typescript"?
> The checker is fully compatible with TypeScript type annotations and can work without any type annotations at all.
That should at least be guarded with a version number, then.
But the solution really is to assume `: unknown` at API boundaries and run the values through assertion functions: `isSomeType(x: unknown): asserts x is SomeType`.
After using this sort of pattern, I don't think I would want automatic runtime checks anymore, because creating your own explicitly and calling it explicitly works out not so bad.
Well, until this is released! https://github.com/microsoft/TypeScript/pull/50666
Just a simple function (ensureType) that checks primitive types (angle brackets for min length). You reconstruct the object using ensureType, writing it back into itself. EnsureType returns what is passed in, with the corresponding type. Has worked well.
I wonder how many of us there are ...
I actually googled for this just yesterday!
- Inferred generics that work
- Effect tracking
- Prepack-style AOT evaluation, but for typechecking
- JSX built in, with proper inference
- Using a typechecker to reduce the amount of work React needs to do (!!!)
- Using a typechecker to make SSR more efficient
- …
These are super interesting and very novel. I hope to see this open sourced soon —- curious if this kind of approach will work at scale.
[1]: https://trpc.io/
(and an example: https://twitter.com/MarcJSchmidt/status/1552781654065905664)
It can sometimes be quite an effort to refactor a medium sized project into project references with incremental builds and there's currently no obvious moment where Typescript knows to tell you "you've got a 'large' project now, if you split this workspace into multiple tsconfig projects that smartly reference each other and switched to incremental build flags you'd get a bunch of performance improvements". So that's still a matter of figuring it out for your own projects if you can take advantage of that (and how you would take advantage of that).
Instead if you like stricter types, `.map`, and `.push` JSX and want to reduce the runtime abstraction by moving it to compile time then it offers a possible alternative to React. Haven't gone into too much yet but there is a probably a lot of React applications that are too complicated for the optimiser to work :( and there it won't be an alternative.
Lots of great alternatives to React these days as well which I also want to promote!
I think 90% of JS types can be inferred statically...
This sentence is unreadable. The superfluous/incorrect use of parens for `.click()`, in combination with this page's style sheet and the way that paragraph wrapped in my browser are all things that didn't help, but eventually I was able to move past it. (It still doesn't make any sense, but I know that part isn't where the weirdness is.) Still unreadable. Bad for something pulled from a written work that's supposed to be a list of definitions you reference.