The problem with generics type erasure is less of an issue though in practice because the ecosystem is generally compiled from typed code and hence the compile-time guarantees reduce the dangers of erasure. This is unfortunately not true in TypeScript where you encounter plain JS all the time (sometimes TypeScript wrappers of dubious quality) causing more havoc. So while _theoretically_ type erasure could be considered having similar problems, in _practice_ it is much more manageable in Java. I guess if the whole JS ecosystem would be TypeScript only it would be less of an issue there as well, but right now it can be messy.
One more addition, there is a subtle but very important difference between how TypeScript's "erasure" works compared to Java's.
In the case of Java, an explicit cast is emitted in the bytecode, which upon execution checks the compatibility of the actual runtime type with the target type during runtime. Yes, this makes it a runtime error compared to a static bytecode verifier/compiler error, but the behavior is well defined and the error does not propagate further from the original mistake.
In comparison, Typescript does not emit "verification code" at the casting site ensuring for example that all asserted fields exist on the runtime object at that point. The result of this is that the type mismatch will be only evident at the point where for example a missing field is accessed - which can be very far from the original mistake.
If you wish, you can consider type issues caused by Java's erasure as runtime, but _defined behavior_, while in TypeScript it is kind-of undefined behavior that can lead to various error symptoms not known in advance.
'pjmlp you do know how Java does generics right?