如何让 Typescript 识别 Apollo 查询的类型

How to make Typescript recognize the type for an Apollo Query

我正在尝试让 Apollo 与 TypeScript 集成。 我有一个 React Class,如下所示:

interface Data {
  allVendors: Array<VendorType>;
}

class AllVendorsQuery extends Query<Data> {}

const ShowVendors: React.SFC<> = props => {
  return (
    <AllVendorsQuery query={fetchVendors}>
      {({ loading, error, data: { allVendors } }) => {
        if (loading) {
          return 'Loading...';
        }
        if (error) {
          return `Error! ${error.message}`;
        }

        return (
          allVendors &&
          allVendors.map((vendor, index: number) => {
            return (
              <div key={`${vendor.name}_${index}`}>
                #<strong>{vendor.id}</strong>
                &nbsp;{vendor.name}
              </div>
            );
          })
        );
      }}
    </AllVendorsQuery>
  );
};

export default ShowVendors;

查询是:

export default gql`
  query GetVendors {
    allVendors {
      id
      name
    }
  }
`;

TypeScript 抱怨 [ts] Type 'Data | undefined' has no property 'allVendors' and no string index signature. 出现在这一行:{({ loading, error, data: { allVendors } }).

但是,如果我使用 apollo-connect 而不是 Query 组件重构代码,我不会收到 TypeScript 的任何抱怨:

import { graphql, compose, QueryResult } from 'react-apollo';

interface ShowVendorsProps {
  data: QueryResult & { allVendors?: VendorType[] };
}

class ShowVendors extends React.Component<ShowVendorsProps> {
  render() {
    const {
      data: { allVendors }
    } = this.props;

    if (allVendors && allVendors.length > 0) {
      return (
        <div>
          {allVendors.map((vendor, index: number) => {
            return (
              <div key={`${vendor.name}_${index}`}>
                #<strong>{vendor.id}</strong>
                &nbsp;{vendor.name}
              </div>
            );
          })}
        </div>
      );
    } else {
      return 'Loading';
    }
  }
}

export default compose(graphql(fetchVendors))(ShowVendors);

两者有什么区别?如何重写第一条语句的类型?

由于您在第一个代码块中使用 data: { allVendors } 解构 data,TypeScript 会抱怨,因为 data 可能未定义,例如当数据仍在加载时。

因此,如果 TS 没有抱怨,您可以在加载检查后解构,使用 allVendors 的默认值,例如:

interface Data {
  allVendors: Array<VendorType>;
}

class AllVendorsQuery extends Query<Data> {}

const ShowVendors: React.SFC<> = props => {
  return (
    <AllVendorsQuery query={fetchVendors}>
      {({ loading, error, data }) => {
        if (loading) {
          return 'Loading...';
        }
        if (error) {
          return `Error! ${error.message}`;
        }

        // If data is not undefined, then it sets allVendors accordingly.
        // Otherwise it sets it to null (which you check for anyways below)             
        const {allVendors} = data || {allVendors: null}; 

        return (
          allVendors &&
          allVendors.map((vendor, index: number) => {
            return (
              <div key={`${vendor.name}_${index}`}>
                #<strong>{vendor.id}</strong>
                &nbsp;{vendor.name}
              </div>
            );
          })
        );
      }}
    </AllVendorsQuery>
  );
};

export default ShowVendors;

有一个小型库和一个 CLI 可以为服务器(根据您的架构)和客户端(根据您的架构和 GraphQL 文档)生成 TypeScript 类型。它还生成解析器签名并且非常可定制。

您可以在这里尝试:https://github.com/dotansimha/graphql-code-generator

这里有一篇关于该软件包的博客 post; https://medium.com/the-guild/graphql-code-generator-for-typescript-react-apollo-7b225672588f

其背后的想法是允许开发人员充分利用 GraphQL 和生成的类型,并更轻松地自定义生成的输出。

它还可以根据您需要的类型生成 react-apollo 组件。