Kan vi definiera generiska typer i TypeScript för typ-predikatfunktioner?


Jag försökte skapa en generisk typ predikatfunktion men jag får ett konstigt fel som jag inte kan hitta mycket information om på Google - så jag undrar om det är möjligt att uppnå följande:

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
}

Lekplats i aktion här.

Jag får följande fel: Type '(session: Partial<Cart<Items<any>>>) => boolean' kan inte tilldelas typen 'SessionHasCart<Items<any>>'. Signatur '(session: Partial<Cart<Items<any>>>): boolean' måste vara ett typpredikat.ts(2322)

Svar #1

Sammanfattning

Ja, du kan definiera typer för predikat med eller utan generics. Men du kan inte tilldela vilken funktion som helst som returnerar ett booleanvärde till det, bara de predikaten.

Den långa versionen

En typ kan inte begränsas vid tilldelning. En predikatfunktion är "mer specifik" än en funktion som returnerar ett booleanvärde.

Se dessa två typer:

type SessionIsCart = (session: Session) => session is Cart;

type SessionTest = (session: Session) => boolean;

Varje SessionIsCart är en SessionTest, men inte tvärtom.

Till exempel:

function IsSessionExplicitlyNotACart(session: Session): boolean {
  // Differentiate between {} and {items: undefined}
  return 'items' in session && session.items === undefined;
}

Den funktionen returnerar också ett booleanvärde, men det är inte ett predikat som kontrollerar om session är en Cart.

Din funktion har ingen signatur, så det härleds från return att den returnerar ett booleanvärde. Den kan tilldelas SessionTest ovan, men inte SessionIsCart. För att funktionen ska bli ett predikat måste du uttryckligen tala om för TS att booleanvärdet som returneras av denna funktion har en speciell betydelse genom att skriva hela signaturen:

const sessionHasCart: SessionHasCart = (session: Session): session is Cart => {
  return (session as Cart).cart !== undefined
}

Eller den generiska versionen

Som @caTS nämnde i kommentarerna, är typpredikat en delmängd av boolean-typen, precis som symbolkonstanter är en delmängd av symbol-typen och stränglitteraler är en delmängd av string-typen. Men de är normala typer.

En typvakt kan också vara generisk och delta i inferens utan några problem.

Detta är ett exempel på att använda typvakter för en funktionell filtreringsstil. Jag använder dessa exakta funktioner på många ställen på jobbet, tillsammans med andra hjälpsamma funktioner som jag har gjort.

Relaterade artiklar