Apollo 的 TypeScript 类型使用Mutation 的 refetchQueries?

Posted

技术标签:

【中文标题】Apollo 的 TypeScript 类型使用Mutation 的 refetchQueries?【英文标题】:TypeScript type for Apollo useMutation's refetchQueries? 【发布时间】:2021-10-04 03:08:14 【问题描述】:

我有一个带有 TypeScript 类型的 Apollo useMutation 钩子:

const MY_MUTATION = gql`
  mutation($source: String!) 
    doSomething(source: $source) 
      result
    
  
`

const [myMutation] = useMutation<ReturnPayloadType, MutationVariables>(MY_MUTATION);

当我使用它时,这给了我类型安全:

myMutation(
  variables: 
    source: 'some source',
    // foo: 'bar' This would error if uncommented. 
  
)

这是有效的,但现在我需要使用 refetchQueries 键。如何向它添加类型?

myMutation(
  variables: 
    source: 'some source',
    // foo: 'bar' This would error if uncommented. 
  ,
  refetchQueries: [
    
      query: MY_OTHER_QUERY,
      variables: 
        foo: 'bar' // I want to add type safety here
      
    
  ]
)

【问题讨论】:

【参考方案1】:

您可以使用 *awaitRefetchQueries¨ 来等待查询的执行:

awaitRefetchQueries: true,
refetchQueries: [
  
    query: YOUR_QUERY,
  ,
],

【讨论】:

【参考方案2】:

简答

你真的不能。从 Apollo Client v3.4.0 开始,refetchQueries 被输入,因此最终解析为:

| "all"
| "active"
|  Array<string | DocumentNode | QueryOptions>`

类型没有类型参数(通用),以允许您为refetchQueries 指定您自己的类型。我会在运行时依赖 GraphQL 的类型保护,并可能在编译时将类型转换作为次要保护。您可以将传递给refetchQueries 的每个对象的类型转换为QueryOptions,并通过QueryOption 的类型参数指定变量的类型。类型转换确实有你需要记住使用它的警告,并且它不保证变量和查询的类型将匹配。

myMutation(
  variables: 
    source: 'some source',
    // foo: 'bar' This would error if uncommented. 
  ,
  refetchQueries: [
    
      query: MY_OTHER_QUERY,
      variables: 
        foo: 'bar'
      
     as QueryOptions< foo: string >
  ]
)

长答案

从 Apollo Client v3.4.0 开始,refetchQueries 选项的类型如下:

  refetchQueries?:
    | ((result: FetchResult<TData>) => InternalRefetchQueriesInclude)
    | InternalRefetchQueriesInclude;

——https://github.com/apollographql/apollo-client/blob/release-3.4/src/core/watchQueryOptions.ts#L223-L225

InternalRefetchQueriesInclude 的输入如下:

export type InternalRefetchQueriesInclude =
  | InternalRefetchQueryDescriptor[]
  | RefetchQueriesIncludeShorthand;

——https://github.com/apollographql/apollo-client/blob/release-3.4/src/core/types.ts#L35-L37

然后上面的内容基本上解析为:

| "all"
| "active"
|  Array<string | DocumentNode | QueryOptions>`

——https://github.com/apollographql/apollo-client/blob/release-3.4/src/core/types.ts#L26-L29

QueryOptions 本身输入为here。

在这些类型中,没有类型参数(通用)允许您为refetchQueries 指定自己的类型。尽管即使泛型可用,您也需要解决以下问题:尝试创建一个强制执行“给定具有 type 属性 query 的对象,我们需要另一个属性 variables 和另一个类型”。为什么这很重要?好吧,要回答这个问题,您需要问输入这个有什么意义?您希望确保在使用给定查询时,任何传入的错误类型的变量都会在编译时而不是运行时被捕获,并降低用户遇到错误的风险。使用函数很容易,因为函数的标识符和类型的标识符是相同的(它内置在函数中,类型说“给定这个函数,它应该有 type 的参数”)但是在我们的例子中,我们没有函数。因此,这就是为什么我以“你不能真的”开头的原因。

我确实探索了利用typeof 的可能性,如下所示:

interface MyQueryNameRefetch extends QueryOptions 
  query: typeof MY_QUERY;
  variables: QueryVariables

在我最初的测试中,我只使用了 const 字符串,并错误地将效果推断为上述可能性。进一步的测试表明,这并没有达到我们想要的效果,因为只有文字类型和枚举成员在使用 const 声明或断言时不会扩大其类型。

你能得到的最接近的是输入变量:

  refetchQueries: [
    
      query: MY_OTHER_QUERY,
      variables: 
        foo: 'bar'
      
     as QueryOptions< foo: string >
  ]

关于 Arvid 答案的说明

此时我想谈谈@Arvid 的answer。有一个功能:

function typedQuery<TVariables>(
  options: QueryFunctionOptions<unknown, TVariables> & 
    query: TypedDocumentNode<unknown, TVariables>;
  ,
) 
  return options;

你会这样使用它:

function MyComponent() 
  const [myMutation] = useMutation<MyMutationPayloadType, MyMutationVariables>(MY_MUTATION);

  myMutation(
    variables: 
      source: 'a',
    ,
    refetchQueries: [
      typedQuery<MyQueryVariables>(
        query: MY_QUERY,
        variables: 
          value: 1,
          // value: 'a' <-- gives compiler error
        ,
      ),
    ],
  );

这与强制转换非常相似,但实际上并不是一个好主意。它有铸造的缺点(你必须记住使用它);以及其他一些,例如具有运行时效果,因为该函数将存在于编译代码以及调用堆栈中(您的类型不应该具有运行时效果)以及对代码理解方式的影响(函数传达一些单位运行时功能,但这里没有运行时功能)。

再次,根本原因归结为我之前描述的关于标识符和类型如何断开连接的问题。

【讨论】:

我不得不不同意你关于返回给定参数的函数的观点,其类型通常是无用的,它实际上与强制转换相同,因此响应似乎有点夸张。我确实同意演员阵容也很好,而且结果几乎相同。我也认为阿波罗可以在他们的类型上做得更好。在我的项目中,我必须解决生成的代码,但如果 TypeScript 可以在编译时 infer the TypeScript type of a Json Schema,它当然可以推断出重新获取查询所需的类型。 我并不是说一个函数仅仅返回它的参数是没有用的。我的意思是,当原因是正确输入某些内容时,这不是一个好主意,尤其是当有更好的构造时。 TypeScript 能够理解 GraphQL 类型是一个单独的问题。这里的问题是 Apollo 提供了一个接口来运行查询,该查询是以queryvariables 作为属性的对象。键入 this 的全部目的是为给定查询强制执行变量类型,但没有语言构造可以键入 this,因此接口并在我的答案中强制转换(简短答案选项 2)。 仅供参考,我的回答发生了重要变化。称为“简答选项 2”的选项无法按预期工作。它将键入您的变量,但不能保证它们将与指定的查询一起使用(首先键入这些变量的要点),因此会给阅读您代码的任何人带来误导。我已经更新了我的答案以反映这一点并分享它为什么不起作用。 @Evanss:如果您已经使用了建议的选项,请按照上述评论将其更改为其他选项。 @Arvid:我认为这种变化证实了没有语言结构可以正确输入(AFAIK)。【参考方案3】:

我对 Apollo 不是很熟悉,但是您可以通过添加辅助函数来解决您的问题。在下面的代码中,我将其命名为 typedQuery。很可能 Apollo 已经内置了这样的功能,但同样,我对这个库还不够了解。

import  gql, QueryFunctionOptions, TypedDocumentNode, useMutation  from '@apollo/react-hooks';

const MY_MUTATION = gql`
  mutation($source: String!) 
    doSomething(source: $source) 
      result
    
  
`;
type MyMutationPayloadType =  result: string ;
type MyMutationVariables = 
  source: string;
;

const MY_QUERY = gql`
  query($value: Int!) 
    findSomething(value: $value) 
      someProperty
    
  
`;
type MyQueryVariables = 
  value: number;
;

function typedQuery<TVariables>(
  options: QueryFunctionOptions<unknown, TVariables> & 
    query: TypedDocumentNode<unknown, TVariables>;
  ,
) 
  return options;


function MyComponent() 
  const [myMutation] = useMutation<MyMutationPayloadType, MyMutationVariables>(MY_MUTATION);

  myMutation(
    variables: 
      source: 'a',
    ,
    refetchQueries: [
      typedQuery<MyQueryVariables>(
        query: MY_QUERY,
        variables: 
          value: 1,
          // value: 'a' <-- gives compiler error
        ,
      ),
    ],
  );

【讨论】:

以上是关于Apollo 的 TypeScript 类型使用Mutation 的 refetchQueries?的主要内容,如果未能解决你的问题,请参考以下文章

使用 Apollo 在 TypeScript 中填充常量时,对象的类型为“未知”.Vetur(2571)

NextJS/Typescript/Apollo 错误;类型上不存在属性

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

如何在 TypeScript 中获取 GraphQL 架构

Apollo 客户端错误 | “RestLink”类型不可分配给“ApolloLink”类型

使用 TypeScript 反应 Apollo useQuery 钩子