EDIT: What I'd like to see now is a "cleanup" hint, which says: hey I already know what type this is, you can remove these 10 lines of aliasing code in this function.
Compare to Typed Racket:
(: foo (-> (U String Null) (U String Null) String))
(define (foo a b)
(cond [(and (null? a) (null? b)) "default"]
[(not (null? a)) a]
At least, in the core language itself, not counting extensions (otherwise Haskell).
I will say that it is a pleasant surprise how flexible and practical TypeScript's type system is.
It would seem that the pressure of working for existing untyped programming patterns has produced something that is very usable and unobtrusive.
Miss it a lot when i have to push some python or golang code.
Except for implicits ha.
(but is in fact correct that Java's type system isn't entirely sound, I forget the details, but I believe array element type variance is one of the soundness hole.)