Summary:
Absolutely, you have the ability to specify types for predicates with or without generics. However, it's important to note that only predicates can be assigned to these types, not just any function that returns a boolean.
Diving Deeper:
When it comes to assignment in TypeScript, a type cannot be narrowed down. A predicate function is considered more specific than a simple boolean-returning function.
Consider the following two types:
type SessionIsCart = (session: Session) => session is Cart;
type SessionTest = (session: Session) => boolean;
Every SessionIsCart
is essentially a SessionTest
, but the reverse is not true.
For instance:
function IsSessionExplicitlyNotACart(session: Session): boolean {
// Differentiating between {} and {items: undefined}
return 'items' in session && session.items === undefined;
}
While this function does return a boolean value, it does not serve as a predicate to determine if the session
is of type Cart
.
If you want your function to act as a predicate, you must explicitly define the signature in TypeScript to indicate that the returned boolean holds special significance:
const sessionHasCart: SessionHasCart = (session: Session): session is Cart => {
return (session as Cart).cart !== undefined
}
As mentioned by @caTS, type predicates are within the boolean
type category, similar to symbol constants falling under the symbol
type and string literals fitting under the string
type. While they are normal types, they possess unique characteristics.
Additionally, it's worth noting that a generic type guard can participate in inference effortlessly.
Here is an example showcasing the usage of type guards for functional filtering purposes. These functions are utilized extensively in my workplace, alongside other custom implementations I've developed.