Reputation: 6035
I'm working with the React Google Login library in TypeScript. It has type bindings for TypeScript, but all the examples are in JavaScript, and I am pretty new to TypeScript.
The setup code looks like this:
<GoogleLogin
clientId="client-id-value"
onSuccess={successResponseGoogle}
onFailure={failureResponseGoogle}
cookiePolicy={'single_host_origin'}
/>
In TypeScript, the signature for the onSuccess callback is:
readonly onSuccess: (response: GoogleLoginResponse | GoogleLoginResponseOffline) => void
The GoogleLoginResponseOffline
type has only one property, code
, where GoogleLoginResponse
has a range of properties to access the details of the authenticated user.
The problem that I am having is that TypeScript will not let me access any of the GoogleLoginResponse properties on the response parameter, saying, for example
"Property 'getBasicProfile' does not exist on type GoogleLoginResponseOffline'"
I have tried the following to cast or check the type of the parameter, but all give errors of one type or another. My function looks like this:
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.
}
}
It looks from the TypeScript documentation as if the if(response instanceof GoogleLoginResponse)
is close, fails in this case because GoogleLoginResponse
is an interface and it needs to be a class.
Please tell me how this is done! I've looked at lots of StackOverflow questions with similar titles, but none cover this.
Upvotes: 4
Views: 1670
Reputation: 37986
You can use in
operator to narrow the type:
For a
n in x
expression, wheren
is a string literal or string literal type andx
is a union type, the “true” branch narrows to types which have an optional or required propertyn
, and the “false” branch narrows to types which have an optional or missing propertyn
const responseGoogleSuccess = (response: GoogleLoginResponse | GoogleLoginResponseOffline) => {
if('googleId' in response) {
const profile = response.getBasicProfile(); // response is of type GoogleLoginResponse here
}
}
You could of course define custom type guard, but using in
operator is much easier in this case. But if you need this in several places this is how type guard can be defined:
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;
Upvotes: 5
Reputation: 1387
What you need is a user defined type guard: https://www.typescriptlang.org/docs/handbook/advanced-types.html#user-defined-type-guards.
In you case you will check the type based on the content of the object coming at run time and not based on its structure which is the before mentioned interface
function isGoogleLoginResponse(response: GoogleLoginResponse|GoogleLoginResponseOffline): response is GoogleLoginResponse {
// you can check more properties here off course
return (response as GoogleLoginResponse).googleId !== undefined;
}
With this in place, you can use the function in a if
condition, then inside the if
you can use the response object as GoogleLoginResponse
typescript will understand that.
Upvotes: 1
Reputation: 7505
Have you tried using a type assertion, e.g.
const responseGoogleSuccess = (response: GoogleLoginResponse|GoogleLoginResponseOffline) => {
if((response as GoogleLoginResponse).googleId){
// do something with (response as GoogleLoginResponse).googleId
}
}
Upvotes: 0