Totally agree. That's kind of what I was going for when I described the hypothetical "true-strict" mode: don't allow the code I'm writing to do bad/unsafe stuff.
And yes, I really wish that TypeScript's non-null assertions actually included a runtime check. Type casts can't really do anything at runtime without a definition of what "being type Foo" actually means, since the types are erased, but you can definitely check if something is null or undefined and throw an error.
I think it would be fair to have the non-null assertion do a runtime check, and if you want the non-null assertion with no runtime code, just use a type cast.
I add a bunch of tests at the top of all JS function - To make sure the parameters are correct. There is no good reason to not write these tests. You can throw friendly errors, and bugs will be detected early.
So you've written a bunch of bespoke type checkers that run at runtime instead of compile-time...
Shouldn't we be able to just have actual static typing instead?
I also use automatic tests, and most of the time it's the tests inside the functions that detect the error - because the tests runs the code. It's much more simple then trying to prove correctness before the program haven't even run once.
Backward compatibility is bridge to whole JS ecosystem. If there was way use “unsafe” old js code within newer language that would be great. But it would bring another set of annoying problems.
I wish there was language that was basically JavaScript/TypeScript, but it gets rid of backwards-compatible JavaScript edge cases and conventions (like, no more undefined, no more {} == ‘0’, no more weird function ‘this’), removes advanced types and makes the type system sound, and (just in case) can actually check typecasts and non-null assertions at runtime. JavaScript and TypeScript are honestly great languages, but they are great languages with major flaws in the name of backwards-compatibility.
Even with strict typing enabled, I write a surprising amount of bugs which ultimately come down to type errors. And not only that but silent type errors, because TypeScript’s casts and not-null-assertions aren’t checked. So often I find that a mistyped or null value managed to slip through a type guard without the compiler saying anything.