Yeah, the satisfies operator is powerful. One thing it allows you to do is to check that one expression is assignable to a type, without actually declaring or coercing the expression to be of that type.
In my experience, here is summary of when you might want to use the various type features:
- Using the "as" keyword tries to coerce/cast an expression to be of some type. This is the least safe, since it overrides proper type checking, at least to some extent. However, in many cases it still does some level of type checking, and it is sometimes necessary if you know something the type checker is unable to prove.
- Declaring an expression to be of some type before initializing it. This is the strictest, and thus most safe, but sometimes it is inconvenient. You might want types to be inferred, which is a really useful thing sometimes, e.g., in a fluent interface when each step constrains types in additional ways.
- The satisfies keyword bridges the gap between the above two: you want to assert/check that the expression is assignable to a certain type (which can be as loose or strict as you want it), whilst still retaining the original type that is inferred for the expression. Unlike "as", you are not overriding the type system. And you are also not actually declaring a type for the expression. I have found this very useful on numerous occasions.
Having said that, there is weird (new?) behavior of the satisfies keyword where appending it to an expressions can cause the the expression itself to fail type-checking internally, where it would otherwise be fine.
This is strange, the basic premise of satisfies is to check if an expression satisfies a type. If there is a type violation at all, it should be at the expression/type boundary, not inside the expression.
It's making it less useful for me.