TypeScript 参数类型是多种类型的联合:如何确定所提供的类型并使用它?

TypeScript parameter type is a union of multiple types: How can I determine which was supplied and work with it?

我正在使用 TypeScript 中的 React Google Login 库。它具有 TypeScript 的类型绑定,但所有示例都在 JavaScript 中,我对 TypeScript 还很陌生。

设置代码如下所示:

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

在 TypeScript 中,onSuccess 回调的签名是:

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

GoogleLoginResponseOffline 类型只有一个 属性、code,其中 GoogleLoginResponse 具有一系列属性来访问经过身份验证的用户的详细信息。

我遇到的问题是 TypeScript 不允许我访问响应参数上的任何 GoogleLoginResponse 属性,例如

"Property 'getBasicProfile' does not exist on type GoogleLoginResponseOffline'"

我尝试了以下方法来转换或检查参数的类型,但都给出了一种或另一种类型的错误。我的函数如下所示:

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.
  }
}

TypeScript documentation 看起来好像 if(response instanceof GoogleLoginResponse) 很接近,在这种情况下失败,因为 GoogleLoginResponse 是一个接口,它需要是 class。

请告诉我这是怎么做到的!我看过很多标题相似的 Whosebug 问题,但 none 涵盖了这一点。

您是否尝试过使用类型断言,例如

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

  if((response as GoogleLoginResponse).googleId){   
    // do something with (response as GoogleLoginResponse).googleId
  }
}

Typescript

你需要的是一个用户定义的类型保护:https://www.typescriptlang.org/docs/handbook/advanced-types.html#user-defined-type-guards.

在这种情况下,您将根据 运行 时出现的对象内容检查类型,而不是根据其结构,即前面提到的接口

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

有了这个,您可以在 if 条件下使用该函数,然后在 if 中您可以使用响应对象,因为 GoogleLoginResponse typescript 会理解这一点。

您可以使用 in 运算符来 narrow the type:

For a n in x expression, where n is a string literal or string literal type and x is a union type, the “true” branch narrows to types which have an optional or required property n, and the “false” branch narrows to types which have an optional or missing property n

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

Playground


您当然可以定义 custom type guard,但在这种情况下使用 in 运算符要容易得多。但是如果你在几个地方需要它,这就是类型保护的定义方式:

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;

Playground