I understand that you said "similar", but there's actually a big difference that should be mentioned explicitly, namely that algebraic data type (ADT) sums always have "constructors" which you can use to disambiguate with. That means that you can meaningfully do the equivalent of "int | int" whereas for union types that would just be "int". (I'm sure you already know this, I'm just pointing this out for those who may not know or appreciate the subtleties.) Example:
data Maybe a = Nothing | Just a
In this example the "constructors" are Nothing and Just.Of course, you can emulate first-order ADT sums using union types by just introducing artifical container classes and doing a union on those. While this works for simple cases, I believe (but cannot prove) that it's impossible to emulate GADTs using union types -- my intuition is that the presence of constructors to match on is an essential part of being able to "narrow" the types sufficiently to actually act upon what they "contain" (for each case).
However, and notwithstanding all of that... the use of union types in TypeScript is absolutely the best way to align with JavaScript since there's so much JS that just takes/returns values of type "whatever" (string | number | ...).
Btw, also agreed on the productivity boost. In the short term, it may not appear that you're getting faster, but once your application starts to grow beyond "trivial" you really start to notice the fact that you can refactor without fear.
I truly feel that "disjoint unions" is one of those things you don't appreciate the true value of until you've used them for quite a while. The real eye-opener for me was implementing a state machine and finding that I could explicitly state exactly which bits of the state machine would propagate to $NEXT_STATE at every step... and have the compiler double-check for me that I got it right.
EDIT: I accidentally a accidentally.
type Maybe<T> = <V>(v:{just(x:T):V, nothing():V}) => V
let just = <T>(x:T):Maybe<T> => <V>(v:{just(x:T):V}) => v.just(x)
let nothing = <T>():Maybe<T> => <V>(v:{nothing():V}) => v.nothing()
let values:Maybe<number>[] = [just(6), nothing()]
values.forEach(maybe => {
alert(maybe({
just: x => 'got: ' + x,
nothing: () => 'nothing'
}))
})Typescript doesn't have real algebraic data types becauses at the end of the day, Typescript is just a statically-typed version of Javascript. And Javascript doesn't have adts.
For me, the biggest advantage of static typing is that code is much more self-documenting. Also, refactoring becomes much easier.
The whole code browsing experience is just different, you can actually read partial sections of the code base and understand them, use code completion, "go to definition" and "find all usages" and it works reliably. Not using types is in my book professional misconduct.
His comparison with C#/Java is also very weird and shows he hasn't given it a proper try. The ducktyping nature of interfaces makes the code much less rigid than in java but still maintains most of the safety you need.
TypeScript definitely got the structural checking of interfaces right, but I beleive (correct me if I'm wrong, I'm referring to https://github.com/Microsoft/TypeScript/issues/202) that classes are also structurally checked. Flow does nominal checking for those, with very good reasons.
While it's a shame the details are light because you kind of need the presenter to go with the slides, the following explains a few of the reasons between TypeScript vs Flow:
http://www.scs.stanford.edu/16wi-cs240h/slides/fb-slides.htm...
(not the whole deck, just a few slides after 45)
I was planning in giving TypeScript a go after having some problems a type system should be able to fix, but after having used just eslint for a while I don't really feel the need any more. Maybe something to reduce the syntax tax of Reacts propTypes would be nice, but then I'm set.
After all, ES is a heavily late bound, dynamically dispatched kind of thing. Which means a truly useful static type system supporting things like statically dispatched type classes and such, would force a completely different semantic on the language. I can't imagine that would be fun to debug in a live browser session.
Actually going back to C# now is a frustrating experience compared to ES6 in just how the type system limits you. Granted that has more to do with nominal typing vs structural, then static vs dynamic. I'm guessing TypeScript don't have the same issues.
By the way, can you extrapolate on eslint and babel working to check symbols across modules? I don't think I ever noticed that feature.
Static types allow for much, much better tooling, particularly autocomplete and the ability to check whether your code is valid on some basic levels. I consider avoiding it to be a big waste of time. I've had a good experience getting the definitions files going for the libraries I use.
I also disagree with the statement that TypeScript is making JavaScript "like C# or Java". TypeScript lets you opt out of type checking all you want with minimal difficulty. It also will by default always emit JavaScript even if it detects type errors.