reactjs - TypeScript parameter typ är en union av flera typer: Hur kan jag avgöra vilken som tillhandahölls och arbeta med den?


Jag arbetar med React Google Login-biblioteket i TypeScript. Det har typbindningar för TypeScript, men alla exempel är i JavaScript, och jag är ganska ny på TypeScript.

Installationskoden ser ut så här:

  <GoogleLogin
    clientId="client-id-value"
    onSuccess={successResponseGoogle}
    onFailure={failureResponseGoogle}
    cookiePolicy={'single_host_origin'}
  />

I TypeScript är signaturen för onSuccess-återanropet:

readonly onSuccess: (response: GoogleLoginResponse | GoogleLoginResponseOffline) => void

Typen GoogleLoginResponseOffline har endast en egenskap, code, medan GoogleLoginResponse har en rad egenskaper för att komma åt detaljerna för den autentiserade användaren.

Problemet jag har är att TypeScript inte låter mig komma åt några av egenskaperna i GoogleLoginResponse på response-parametern, och säger till exempel

"Egenskapen 'getBasicProfile' finns inte på typen GoogleLoginResponseOffline"

Jag har försökt följande för att kasta eller kontrollera typen av parametern, men alla ger fel av något slag. Min funktion ser ut så här:

const responseGoogleSuccess = (response: GoogleLoginResponse|GoogleLoginResponseOffline) => {

  // Tried to check for property to identify type
  if(response.googleId){    // Property 'googleId' does not exist on type 'GoogleLoginResponseOffline'
      const profile = response.getBasicProfile();  // Property 'getBasicProfile' does not exist on type 'GoogleLoginResponseOffline'
  }

  // Tried to cast like this
  const typedResponse = <GoogleLoginResponse>response;

  // Tried to check type
  if(response instanceof GoogleLoginResponse){   // 'GoogleLoginResponse' only refers to a type, but is being used as a value here.
  }
}

Det ser ut från TypeScript-dokumentationen som om if(response instanceof GoogleLoginResponse) är nära, men misslyckas i detta fall eftersom GoogleLoginResponse är ett gränssnitt och det behöver vara en klass.

Snälla, berätta för mig hur detta görs! Jag har tittat på många StackOverflow-frågor med liknande titlar, men ingen täcker detta.

Svar #1

Du kan använda in-operatören för att begränsa typen:

För ett n in x-uttryck, där n är en strängliteral eller strängliteraltyp och x är en unionstyp, smalnar "true"-grenen av till typer som har en valfri eller obligatorisk egenskap n, och "false"-grenen smalnar av till typer som har en valfri eller saknad egenskap n.

const responseGoogleSuccess = (response: GoogleLoginResponse | GoogleLoginResponseOffline) => {
  if('googleId' in response) {
      const profile = response.getBasicProfile(); // response is of type GoogleLoginResponse here
  }
}

Lekplats

Du skulle naturligtvis kunna definiera en anpassad typvakt, men att använda in-operatorn är mycket enklare i det här fallet. Men om du behöver detta på flera ställen är detta hur en typvakt kan definieras:

type Reposense = GoogleLoginResponse | GoogleLoginResponseOffline;

const responseGoogleSuccess = (response: Reposense) => {
  if (isLoginResponse(response)) {
    const profile = response.getBasicProfile();
  }
}

const isLoginResponse = (response: Reposense): response is GoogleLoginResponse =>
  'googleId' in response;

Lekplats

Svar #2

Det du behöver är en användardefinierad typvakt: https://www.typescriptlang.org/docs/handbook/advanced-types.html#user-defined-type-guards.

I ditt fall kommer du att kontrollera typen baserat på innehållet i objektet som kommer vid körning och inte baserat på dess struktur, vilket är det tidigare nämnda gränssnittet

function isGoogleLoginResponse(response: GoogleLoginResponse|GoogleLoginResponseOffline): response is GoogleLoginResponse {
    // you can check more properties here off course
    return (response as GoogleLoginResponse).googleId !== undefined; 
}

Med detta på plats kan du använda funktionen i ett if-villkor, och sedan inuti if kan du använda svarobjektet som GoogleLoginResponse, TypeScript kommer att förstå det.

Relaterade artiklar