Languages like Rust, Haskell or ML do not have this issue since they do not need to support such a legacy layer.
You can override TypeScript's checks for anything, not just nulls, and yet people still get tons of benefit from it. Same goes with many of Rust's safety checks in unsafe { } blocks.
Additionally, languages like C# and typescript have to be a little more flexible because they added this feature after the fact. Go had plenty of opportunity to do it up front and not require any compromises, and it chose not to.
Still, typescript equips you to catch these errors, even if you can technically circumvent it. In practice it can be nearly bullet-proof if you follow good practices.
If you're using either a library that wasn't written in pure TS (maybe JS or JS with .d.ts) or interacting with some unconverted JS from your own codebase, you can easily pass a null through entirely by accident. The problem really stems from the JS end of things, but 9 times out of 10 you're going to be touching JS at _some_ level when using TS so I think it's fair to point out this gap.
- !
- as x
- @ts-ignore
This isn't to say it's useless, just that it's not as useful as it _could_ be. I've seen codebases in all three languages where there have been real production issues due to null values, even though the type decls say otherwise. Kotlin probably does the best job here but using any JVM library introduces potential (K)NPEs.