Problems with TypeScript in 2020
executeprogram.com
executeprogram.com
If that’s the worst people can levy at TypeScript I’d say it’s a pretty danged good language.
What I hate about TypeScript is that it’s made me feel that all other programming languages’ type systems are inadequate in comparison (union and intersection types, Utility types, immutable and read-only views of existing objects, type guards for duck-typing, etc).
It's also easy to argue that Typescript (and Flow, not to ignore its contributions to the space) happened upon exactly the right sort of evolutionary conditions to both need a lot of the complicated features and present them in such a way that they appealed to a "mainstream" audience: a very dynamic base language with a giant corpus of highly varied real world usage, and a growing need for practical "taming" for large real world applications by large corporations (with vested programming language interests).
It's a credit to Typescript (and to Flow as well) that a lot of the complexity in its type system, often designed as best as it could to handle describing the complexity of real world Javascript usage, seems after the fact as "perfectly natural" enough that now in retrospect people start to wonder why more languages don't adopt the same ideas. Sometimes the simple answer is "because they weren't forced to in order to scratch a present need". As people have access to the tools it becomes easier to see "oh, this complex thing that solves this need over here becomes a useful thing to better these nice-to-haves over there".
Additionally, I don't believe Swift supports true structural typing (i.e. Duck-typing). If you have a method that accepts a protocol-implementing object, that object has to explicitly implement that protocol, it can't be inferred by the compiler.
In a similar vein, C# also supports intersection type (but only intersection-of-interfaces in type-parameterized generic methods) or structural-typing (again, via generics with explicit implementation). Please correct me if I'm wrong, but to my knowledge Swift doesn't support call-site structural typing nor true intersection types (i.e. intersection of non-interface types).
Among those mainstream languages, not many of them having such an advanced type system, with very few exceptions like Scala - even with GADT, dependent types ahead. It's light years ahead of its peers.
But I believe there are still some abstractions can be expressed in TypeScript but not Scala because the gradually/dynamically typed languages start from the opposite side from the spectrum.
It already has a strict mode; it's time for an even stricter mode that simply cuts loose from javascript and flat out removes access to all the little compromises with strong typing that exist only because of javascript. It's a compilation target and with WASM emerging, even that is not a long term certainty.
IMHO the difference between e.g. Kotlin and Typescript is becoming smaller and smaller. I do both. There's been a cross over of lots of nice language features from Kotlin and other languages to Typescript. But at this point, Kotlin is a still a lot cleaner/safer than Typescript even though both can transpile to javascript and it also has a bit nicer ergonomics in some areas. E.g. data classes, property and interface delegation, co-coutines, etc. are all things that would be highly useful in Typescript as well. Android developers seem to love these features. And as Kotlin transpiles to javascript, there's no question about whether or not that can be done because it's already been done.
In any case, most frontend projects have been using transpilers for quite some time now to deal with compatibility issues. It's browser JS is increasingly used as a compilation target. The exception is smallish websites where people don't care about older browsers or using new js features not yet shipped to browsers. Those exist, but it's becoming a minority.
For the rest of us, browser JS is something ugly and minified that webpack spits out and that you probably rarely even look at.
Typescript strikes a great compromise between enough flexibility to get out of your way + type checking to keep you from hanging yourself. Its also easy as hell to learn. I can hire a js engineer and upgrade them into a typescript one in a day or two.
1) they allow for "anonymous types", suppose a function can receive a List or a Dictionary, you could define a type List_or_Dict or (Either List Dict) or Result<List,Dict>, but all of them require you to either have the same sum type at the call site or declare explicitly whether you have a List or a Dict by choosing a type constructor at call site, thus breaking the polymorphism of the union List|Dict (where `|` means union). Another similar example would be with a variable x of type List|Dict and a function f that again accept a String|List|Dict, with union types the call f(x) is perfectly valid.
2) the second advantage is that now you can treat constructors as their own type and define type with arbitrary combinations of them. For example you could define three constructor Ok: a -> Ok a, Err: a -> Err a, None: None and then have an option type defined as Ok a | None and a result type defined as Ok a | Err b. in this situation there would be no need to convert success type of an option into success type of a result as they are already the same type.
This makes type inference extremely more complex but ocaml shows that it can be done (this actually negates my initial point)
Still the usability feature of being able to quickly create arbitrary union types is undeniable (Julia uses something similar as a main source of polymorphism and personally I think it is amazing)