如何使用带有 Typescript 的泛型将中继(graphql)连接映射到边缘节点

Posted

技术标签:

【中文标题】如何使用带有 Typescript 的泛型将中继(graphql)连接映射到边缘节点【英文标题】:How to map a relay (graphql) connection to an edge's nodes using generics with Typescript 【发布时间】:2020-01-26 03:19:21 【问题描述】:

我最近一直在试验符合 React、GraphQL、Apollo 和 Relay 的查询。

现在我有一个 GraphQL 后端,它有一个简单的 GraphQL 架构和一些连接查询,用于返回可以使用中继服务器规范进行分页的实体列表。

在前端,我发现自己在提出上述问题后会重复自己 代码通常如下所示:

type isDefined = <T>(value: T | null | undefined): value is T => value !== null && value !== undefined;

export const AppleListComponent: React.FC = () => 
  const  data  = useAppleQuery(); // FWIW, generating this with graphql-code-generator
  const apples = data && data.apples;
  const appleNodes = apples && apples.edges.map(apple => (
    apple ? apple.node : undefined
  )).filter(apple => isDefined(apple));

  return (
    <div>
      appleNodes && appleNodes.map(apple => (
        <p key=apple.text>apple.text</p>
      ))
    </div>
  );
;

由于 Relay 规范要求边和节点为 Nullable,因此检查存在性是必要的。

我发现自己经常重复这种从中继格式到简单节点数组格式的转换。 我决定看看是否可以创建一个辅助函数,该函数接受符合连接接口的内容并返回未定义或映射节点列表。

那个尝试看起来像这样:

type Maybe<T> = T | null;
type isDefined = <T>(value: T | null | undefined): value is T => value !== null && value !== undefined;

type Connection<T> = Maybe< edges: Array<Maybe< node: Maybe<T> >>>;

export const connectionToNodes = <T>(connection: Connection<T>): Maybe<T[]> => (
  connection && connection.edges.map(edge => (
    edge ? edge.node : undefined
  )).filter(isDefined)
);

这让之前的组件变成

export const AppleListComponent: React.FC = () => 
  const  data  = useAppleQuery();
  const apples = data && data.apples;
  const appleNodes = connectionToNodes(apples);

  return (
    <div>
      appleNodes && appleNodes.map(apple => (
        <p key=apple.text>apple.text</p>
      ))
    </div>
  );
;

我的问题是我的 Connection 类型当然不完全匹配 加上我传入的类型,它有更多看起来像这样的属性:

Maybe<(
   __typename?: 'AppleConnection' 
  &  edges: Array<Maybe<(
     __typename?: 'AppleEdge' 
    &  node: Maybe< __typename?: 'AppleType' 
      &  __typename?: 'QuestionType' 
        & Pick<QuestionType, 'text'>
    > 
  )>> 
)>

所以我最终会收到关于任何类型问题的错误,以及 我传递给connectionToNodes 的内容与指定的类型不匹配。

我的直觉是我需要说我的类型 T 是其他东西的子类型 - 的 申请课程我不在乎是Apple还是Pear 连接/边缘/节点,但我希望它最终被推断出来 - 任何建议 如何实现这一目标?

【问题讨论】:

【参考方案1】:

试试这个助手从连接中提取节点

export type ExtractNode<T extends  edges: any > = NonNullable<NonNullable<T['edges'][number]>['node']>;

用法

type Post = ExtractNode<FeedList_query['posts']>

【讨论】:

以上是关于如何使用带有 Typescript 的泛型将中继(graphql)连接映射到边缘节点的主要内容,如果未能解决你的问题,请参考以下文章

C#中的泛型是啥意思?

TypeScript Vue 组件中的泛型

TypeScript 中具有泛型类型参数的泛型类型的替代方案

TypeScript中的泛型

在 Typescript 的泛型类中初始化泛型类型

选择Typescript的原因:支持定义执行时的泛型参数