Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can we define generic types in TypeScript for type predicate functions?

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)

like image 695
Nicolas Bouvrette Avatar asked Oct 21 '25 03:10

Nicolas Bouvrette


1 Answers

TL;DR;

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.

The long version

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.

like image 158
Luiz Ferraz Avatar answered Oct 23 '25 16:10

Luiz Ferraz



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!