More than once I've yelled at my screen, "HOW DO YOU KNOW THIS?!" about some inferred or narrowed type.
declare foo: never[] // it can only be an empty array
foo[1] // compiles
TypeScript is a nice type system, it's surprisingly expressive and flexible, like e.g. in Haskell I was surprised that there's this strange notion that for a given type you can only have one `Eq` or `Ord` instance for some type A.
But in TypeScript you can have as many Ord interfaces as you want. I may want to order some `User` type by last login, first login or whatever I want, but in Haskell you need to overcomplicate it and create a newtype.
This is a good thing - it stops you from using one Ord instance when putting As into a Map or Set, and then trying to pull them back out using another. Newtypes let you provide "additional" instances in a safe way.
I'll give you a trivial example I have some data type representing a TvShow, why should there be just one way to sort a collection of TvShow? Maybe I want to sort by year, maybe by length, maybe by category.
It's trivial to do in fp-ts, you can have as many Ord instances as you wish.
If your type doesn't have a natural ordering (as your TvShow doesn't) then you would use sortBy to supply an arbitrary compare function (just like what you would define in your Ord instance).
Ordering (and more specifically a total ordering) in mathematics is a set and a binary relation that has the transitive, irreflexive and connected properties.
Not even natural numbers have "uncontroversial natural ordering", as I guess even you can think of at least two different binary relations (<= and >=) that form two different orderings for N.
There's a lot of orderings more, here's one that sorts first odd and even numbers: "0 < 2 < 4 < 6 < ... < 1 < 3 < 5 < 7 < ...". And there's others like this.
https://en.wikipedia.org/wiki/Gray_code
Natural numbers in fact have (or better: form) an aleph one types of orderings. None is "special, uncontroversial or natural" just because we're used to think about the default <=.
The concept of ordering requires two things: the data type and the binary relation. It's a pair of things we can represent as (X, compare) but which compare you choose isn't implicit, natural or magical.
When talking about Haskell, it has the concept of type classes, where types and their behavior are bundled together. That's a design choice of Haskell which has pros and cons, but there's no mathematical foundation for a data type to have one preferred ordering.
Ocaml, Scala and most pure fp libraries I know do not have the limitations of Haskell when it comes to the concepts of ordering and equality, in fact in most of those when asking for an ordering ask you for a data type and a compare function. Exactly what the mathematical definition requires you to.
Not a typeclass where those are bundled together for some design decision.
Hope I clarified you that there's no such thing as "uncontroversial natural obvious" ordering, because there's no such things in mathematics.
declare foo: never[0]; declare foo: []; // fixed-length array (tuple) without any itemsAlthough i don’t know if TypeScript asserts that x[i] is T | undefined if x is T[] and the length is non-trivial. In strict mode this would be really useful as you can just do x[i]!, but it requires you to think a bit about index-out-of-bounds or at least throw on the undefined case.
There's a flag for that: https://www.typescriptlang.org/tsconfig#noUncheckedIndexedAc...
https://www.typescriptlang.org/play?#code/CYUwxgNghgTiAEYD2A...
const a = foo[1]
should result in a bring of type `never|undefined` or `undefined` depending on flow analysis
I don't think that's accurate. Typescript only has one instance of Ord per type and it's inconveniently implemented through the method toString.
To usefully sort anything other than strings in Typescript, you are using a function where you provide your own comparator at runtime. That's more analogous to Haskell's `sortBy :: (a -> a -> Ordering) -> [a] -> [a]` or `sortOn :: Ord b => (a -> b) -> [a] -> [a]` which don't put any type bounds on the type you're sorting.
const x: Thing = exprA;
const y: Thing = exprB;
const z = [x, y];
but somehow not: const z: Thing[] = [exprA, exprB];
although exprA and exprB were pretty nasty typing wise.Doesn't
const z = [exprA, exprB] as const;
type GnarlyThingTuple = typeof z;
or something like this help?TS is sometimes weird because it is a superset of JS and has to be compatible.
I think that's exactly why the author wrote this:
> Eventually, I realized that rewriting a massive project such as tsc would be extremely difficult to continue. But the performance gains were exciting. This led me to try another route: porting instead of a complete rewrite. So, I started looking at the TypeScript source code.
It will require ongoing work to keep up with changes to upstream, but by matching upstream's structure as closely as possible (given the language difference), the path to supporting these nasty little edge cases should be relatively clear.
As with so many terms in computing, there aren't universally agreed definitions. But in the author's post, I read "porting" as switching languages while changing as little as possible vs "complete rewrite" as redesigning/rearchitecting it along the way.
And in comparison, a "complete rewrite" would require a full understanding of the big picture design of the original code, come up with a perhaps different one in the rewrite, and keep the differences in your head as you go (or, forget about the original design entirely and solve it from scratch - and keep that in your head as you go). Far more work and risk, and it would have been a bad choice here.
I would personally be very happy to use a significantly slimmed down subset of TypeScript if it provided a 62x performance improvement. Slow type checking is currently the main drawback of TypeScript development in large projects.
> I would personally be very happy to use a significantly slimmed down subset of TypeScript if it provided a 62x performance improvement. Slow type checking is currently the main drawback of TypeScript development in large projects.
We all think that until the slimmed down subset doesn't work with our favorite libraries. Sure, there may be people who use no dependencies, but I'd bet that there's sophisticated types in some very popular libraries that even low dependency programmers use.
I hadn't heard of this trick - how much improvement does it make? Seems like it might be good for readability, too?
The Typescript compiler performance wiki might also be of interest: https://github.com/microsoft/TypeScript/wiki/Performance
So the only possibility I see is a webassembly alternative.
I'm just guessing but I think a lot of Typescript's really complex stuff is to support third party libraries with really complex types - at least they often refer to libraries they've improved support for when they discuss complex seeming additions. So I wonder how much of the JS ecosystem you'd have to cut off to get to such a subset. A particular example would be that I've often seen a chain of 20+ dependencies leading to Lodash, and I'd guess that Lodash is really complex to type. If you lose everything that's eventually dependent on something that's really complex to type, do you have much of NPM left?
Tanner Linsley (author of React Table, React Query, and other similar libs) has expressed similar concerns.
(I'm actually starting to work on a talk for the upcoming TS Congress conference about "lessons learned maintaining TS libs", and this will be one of the things I point to.)
Isn’t that already available by running in “transpile only” and having the type checking happen in the background?
edit: I see they are running it with 8 threads, in which case yes 50x is achievable. In any sizable codebase though, you should probably split into modules and run with some parallelism via e.g. `wsrun` or `turborepo`
tsc has in fact been designed with speed in mind (I've been following the project since before it moved to GitHub, which is when they redesigned it for speed). Going beyond 1 order of magnitude performance improvement while implementing the full feature set is highly unlikely.
I would bet on a port eventually being done and hitting at least 10X.
It’s a form of linting, not semantics, so it doesn’t have to be exact.
This is true of many programming languages projects. It's not uncommon for someone to write a language in a weekend, and then marvel at how their very simple toy language is faster than C! And look how quickly it compiles! Thus begins a several years-long journey, where with every feature added the runtime and compiler get slower and slower and slower.
That means if this implementation passes all TypeScript's tests, it is as good as TypeScript.
Each new issue that is opened in either repo that shows discrepancy can be a new unit test added to the TypeScript codebase.
The only thing is it may not catch some type error, which JS already does none of and TS can skip wherever you want or even implicitly and people are fine with that.
If I get 98% of TS typechecking 10 times faster. It's a huge win.
Give the people a turing complete type system and they will write a complete application with it.
Saving a minute every cycle sounds very attractive, but when a person spends a morning hacking and then finds out the normal tool chain doesn't accept their work when the optimized one did, they will quickly distrust it in the future. It'll also make them very reluctant to share it with their team.
The only reason my project was successful was we ultimately reached a set of compromises with the team that owned the woefully underspecified tool chain and upstreamed large swaths of changes. I would probably never do something like that again.
You need a test suit for this not a spec.
Presumably TypeScript has a test suite already that might serve as a starting point?
TypeScript's compiler is an unholy, unsound mess, because JavaScript is an unholy, unsound mess. Sure the basics of type checking structs and variables is somewhat easy. But what about conditional types? What about weird generic constraints? What about indexing types? There's some really confusing stuff going on underneath the hood.
It's also a mess that *does not have a spec*. Even if you somehow manage to make a type checker that implements most of the features, there's no way to ensure it has 1 to 1 parity with TypeScript. Plus that parity can be broken at any point.
The only way I could see this happening is if the author somehow convinced Anders Hejlsberg or a significant portion of the TypeScript team to work on this new implementation. I'm not sure that's gonna happen.