I was trying to create a generic type predicate function but I am getting an odd error which I cannot find much information on while Google - so I am wondering if it's possible to achieve the following:
type Items<T = any> = { [item: string]: T }
type Cart<T = any> = {
cart: Items<T>
}
type OptionalCart<T = any> = Partial<Cart<T>>
type SessionHasCart = <T extends Items = Items>(session: OptionalCart<T>) => session is Cart<T>
const sessionHasCart: SessionHasCart = (session) => {
return (session as Cart).cart !== undefined
}
Playground in action here.
I am getting the following error: Type '(session: Partial<Cart<Items<any>>>) => boolean' is not assignable to type 'SessionHasCart<Items<any>>'. Signature '(session: Partial<Cart<Items<any>>>): boolean' must be a type predicate.ts(2322)
Yes, you can defined types for predicates with or without generics. But you cannot assign any function returning a boolean to it, only those predicates.
A type cannot be narrowed on assignment. A predicate function is "more specific" than a function returning a boolean.
See these two types:
type SessionIsCart = (session: Session) => session is Cart;
type SessionTest = (session: Session) => boolean;
Every SessionIsCart is a SessionTest, but not the opposite.
For example:
function IsSessionExplicitlyNotACart(session: Session): boolean {
// Differentiate between {} and {items: undefined}
return 'items' in session && session.items === undefined;
}
That function also returns a boolean, but it is not a predicate that checks if the session is a Cart.
Your function has no signature, so it is inferred from the return that it returns a boolean. It is assignable to the SessionTest above, but not SessionIsCart. For the function to become a predicate, you must explicitly tell TS that the boolean returned by this function has a special meaning by writing the entire signature:
const sessionHasCart: SessionHasCart = (session: Session): session is Cart => {
return (session as Cart).cart !== undefined
}
Or the generic version
As @caTS mentioned in the comments, type predicates are a subset of the boolean type, much like symbol constants are a subset of the symbol type and string literals are a subset of the string type. But they are normal types.
Also, a type guard can be generic and take part in inference without any problems.
This is one example of using type guards for a functional filtering style. I use these exact functions in many places at work, along with other helpful functions that I made.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With