0.2 + 0.1 === 0.3

That's not really a JS problem, that's a floating point problem. Plenty of languages will have the same issue.

+!![]

"" - - ""

(null - 0) + "0"

Calling these things weird is fair enough but I can't help thinking this is code you'd never actually write outside of the context of a "Look how weird JS is!" post. It's like picking examples from the Annual Obfuscated C Contest to show how hard it is to understand C. Yes, some languages enable you to write weird garbage code that's hard to reason about, and ideally they wouldn't because enforcing sane code would be great, but come on. Most of these things aren't a big problem that we suffer every day. Just be moderately careful about casting things from one data type to another and all these problems go away.

I think the situation is a bit different. This situation looks different in reality. The result may be (null-0)+"0", but the actual code will be foo()-bar()+baz(). And C will at least give you warning about types, even if NULL-0+"0" could give you an address. Plain JS without extra tooling would happily give you the unexpected result. Some other dynamic languages would at least throw an exception about incompatible types for -/+.

Yeah guess this is where Typescript comes in

TypeScript wont magically fix type errors. It is not that hard, to sanitize any expected parameter for functions and their output, typescript wont do that for you. So if you typecheck i/o values per se, using typescript only slows down dev process, as this can be easily done in vanilla javascript. no need for more bloat, but only for some defensive programming.

TypeScript "magically" fixes the need for defensive programming by ensuring you won't write unsanitary code or invoke functions with possibly null&undefined values by accident. So then clearly the defensive programming is the bloat, because you could avoid it entirely by putting a type system in place to prevent you ever putting yourself in a situation where null&undefined get passed to functions you don't want it to be.

This falls apart the moment you're pulling remote data at runtime. You're right back to defensive programming since there's no more type system to help you at that point.

That's nowhere near "falling apart". That's just the simple fact there's no silver bullet.

Someone arguing a little defensive programming is equivalently strong to a type system is clearly unaware just how much work a good type system does for you. I of course agree with you: when you're fetching data that you don't know the type of, recklessly casting it to some type is going to cause issues. This is true in every language that has ever existed. It's also why tools like IO-TS[0] exist, and of course you can enforce this with JSON Schema techniques or custom validators or a million other options.

Edit: in case the ultimate point of this comment is not clear, by using a type system and some type validator on your fetches, you are able to reduce the need for defensive programming exclusively to your fetch points. Clearly, defensive programming at the fetch points was already needed, so this is why I do not agree with the claim TypeScript's value add disappears from remote fetches.

[0]: https://github.com/gcanti/io-ts