On as, since "almost never" deserves the exceptions and the reason.
A cast does not convert anything. It changes what the compiler believes and nothing else, so if you are wrong the error simply moves to runtime, where there is no type system to catch it. Every cast is a claim you are making on your own authority.
The legitimate uses are narrow and they share a shape - you know something the compiler cannot:
- After a runtime validation the compiler cannot follow, though a type predicate or a validator that returns a typed result is better and removes the need
- Narrowing a broad type from an external boundary you have just checked
as unknown as Tfor genuinely untyped interop, which is at least honest about being a two-step lie
What it should never be is the fix for an error you did not understand. That is the common case and it is how a codebase accumulates assertions that were true when written and are not any more: nothing rechecks them, so they rot silently.
A practical habit: when you write a cast, write a comment saying why the compiler cannot know this. If you cannot finish the sentence, you have found a bug rather than a typing inconvenience. And if you are casting an object literal specifically, the answer is essentially always satisfies instead.