译零 TransZero · TanStack Query 中文文档
主站

TypeScript

更新于 2026-11-02 Angular 查看英文原文 ↗

TanStack Query 现已使用 TypeScript 编写,以确保该库及你的项目具备类型安全性!

需注意以下几点:

  • TanStack Query 遵循 DefinitelyTyped 的支持窗口,支持过去两年内发布的 TypeScript 版本。目前这意味着支持 TypeScript 5.7 及更高版本。
  • 本仓库中类型定义的变更被视为 非破坏性变更,通常以 补丁(patch) 语义化版本号发布(否则每次类型增强都将触发主版本升级!)。
  • 强烈建议你将 angular-query-experimental 包版本锁定到特定补丁版本,并在升级时预期类型可能在任意版本间被修复或升级。
  • TanStack Query 的非类型相关公共 API,以及实验阶段结束后正式发布的 angular-query 包,仍严格遵循语义化版本规范。

类型推断

TanStack Query 中的类型通常能顺畅流动,因此你通常无需手动提供类型注解。

angular-ts
1@Component({2  // ...3  template: `@let data = query.data();`,4  //               ^? data: number | undefined5})6class MyComponent {7  query = injectQuery(() => ({8    queryKey: ['test'],9    queryFn: () => Promise.resolve(5),10  }))11}
angular-ts
1@Component({2  // ...3  template: `@let data = query.data();`,4  //               ^? data: string | undefined5})6class MyComponent {7  query = injectQuery(() => ({8    queryKey: ['test'],9    queryFn: () => Promise.resolve(5),10    select: (data) => data.toString(),11  }))12}

若你的 queryFn 具有明确定义的返回类型,则此机制效果最佳。请注意,大多数数据拉取库默认返回 any,因此请确保将其提取为具有正确类型的函数。

本示例中,我们将 Group[] 作为类型参数传入 HttpClient 的 get 方法。

angular-ts
1@Component({2  template: `@let data = query.data();`,3  //               ^? data: Group[] | undefined4})5class MyComponent {6  http = inject(HttpClient)7 8  query = injectQuery(() => ({9    queryKey: ['groups'],10    queryFn: () => lastValueFrom(this.http.get<Group[]>('/groups')),11  }))12}

类型缩小

TanStack Query 对查询结果采用 可辨识联合类型(discriminated union type),其区分字段为 status 字段及派生的状态布尔标志。这使你能通过检查例如 isSuccess() 状态,来确保 data 已定义:

angular-ts
1@Component({2  // ...3  template: `4    @if (query.isSuccess()) {5      @let data = query.data();6      // ^? data: number7    }8  `,9})10class MyComponent {11  query = injectQuery(() => ({12    queryKey: ['test'],13    queryFn: () => Promise.resolve(5),14  }))15}

TypeScript 当前不支持对对象方法进行可辨识联合类型判断。对查询结果等对象上的信号字段进行类型缩小,仅在该信号返回布尔值时有效。请优先使用 isSuccess() 及类似布尔状态信号,而非 status() === 'success'。

错误字段(error field)的类型定义

error 字段的默认类型为 Error,因为这是大多数用户的预期。

angular-ts
1@Component({2  // ...3  template: `@let error = query.error();`,4  //                ^? error: Error | null5})6class MyComponent {7  query = injectQuery(() => ({8    queryKey: ['groups'],9    queryFn: fetchGroups,10  }))11}

若你想抛出自定义错误,或完全不是 Error 类型的错误,可显式指定 error 字段的类型:

angular-ts
1@Component({2  // ...3  template: `@let error = query.error();`,4  //                ^? error: string | null5})6class MyComponent {7  query = injectQuery<Group[], string>(() => ({8    queryKey: ['groups'],9    queryFn: fetchGroups,10  }))11}

但此举的缺点是:injectQuery 所有其他泛型的类型推断将不再生效。通常认为,抛出非 Error 类型的值并非良好实践;因此,若你拥有类似 AxiosError 的子类,可借助 类型缩小 使 error 字段更具体:

TypeScript
1import axios from 'axios'2 3query = injectQuery(() => ({ queryKey: ['groups'], queryFn: fetchGroups }))4 5computed(() => {6  const error = query.error()7  //     ^? error: Error | null8 9  if (axios.isAxiosError(error)) {10    error11    // ^? const error: AxiosError12  }13})

注册全局错误类型

TanStack Query v5 提供一种方式,通过扩展 Register 接口,在调用端无需指定泛型的情况下,为所有内容设置全局错误类型。这可确保类型推断依然有效,同时 error 字段将采用指定类型。若你希望强制调用端必须执行显式类型缩小,请将 defaultError 设为 unknown:

TypeScript
1import '@tanstack/angular-query-experimental'2 3declare module '@tanstack/angular-query-experimental' {4  interface Register {5    // Use unknown so call sites must narrow explicitly.6    defaultError: unknown7  }8}9 10const query = injectQuery(() => ({11  queryKey: ['groups'],12  queryFn: fetchGroups,13}))14 15computed(() => {16  const error = query.error()17  //      ^? error: unknown | null18})

meta 字段的类型定义

注册全局 meta 类型

与注册 全局错误类型 类似,你也可注册全局 Meta 类型。这可确保 查询 和 修改 上的可选 meta 字段保持一致且类型安全。注意:所注册的类型必须继承 Record<string, unknown>,以保证 meta 始终为对象。

TypeScript
1import '@tanstack/angular-query-experimental'2 3interface MyMeta extends Record<string, unknown> {4  // Your meta type definition.5}6 7declare module '@tanstack/angular-query-experimental' {8  interface Register {9    queryMeta: MyMeta10    mutationMeta: MyMeta11  }12}

查询键与修改键的类型定义

注册查询键与修改键类型

同样地,除注册 全局错误类型 外,你也可注册全局 QueryKey 和 MutationKey 类型。这使你能为键提供更契合应用层级结构的组织方式,并在整个库的各处接口中保持类型安全。注意:所注册的类型必须继承 Array 类型,以确保键始终为数组。

TypeScript
1import '@tanstack/angular-query-experimental'2 3type QueryKey = ['dashboard' | 'marketing', ...ReadonlyArray<unknown>]4 5declare module '@tanstack/angular-query-experimental' {6  interface Register {7    queryKey: QueryKey8    mutationKey: QueryKey9  }10}

查询选项(Query Options)的类型定义

若你在 injectQuery 中内联查询选项,将自动获得类型推断。但你可能希望将查询选项提取至独立函数,以便在 injectQuery 与例如 queryClient.query 之间共享,或在服务中统一管理。此时,你将丢失类型推断。为恢复推断能力,可使用 queryOptions 辅助函数:

angular-ts
1import { noop } from '@tanstack/angular-query-experimental'2 3@Injectable({4  providedIn: 'root',5})6export class QueriesService {7  private http = inject(HttpClient)8 9  post(postId: number) {10    return queryOptions({11      queryKey: ['post', postId],12      queryFn: () => {13        return lastValueFrom(14          this.http.get<Post>(15            `https://jsonplaceholder.typicode.com/posts/${postId}`,16          ),17        )18      },19    })20  }21}22 23@Component({24  // ...25})26export class Component {27  queryClient = inject(QueryClient)28 29  postId = signal(1)30 31  queries = inject(QueriesService)32  optionsSignal = computed(() => this.queries.post(this.postId()))33 34  postQuery = injectQuery(() => this.queries.post(1))35  postQuery = injectQuery(() => this.queries.post(this.postId()))36 37  // You can also pass a signal which returns query options38  postQuery = injectQuery(this.optionsSignal)39 40  someMethod() {41    void this.queryClient.query(this.queries.post(23)).catch(noop)42  }43}

此外,queryOptions 返回的 queryKey 会知晓与其关联的 queryFn 类型,我们可利用该类型信息,使 queryClient.getQueryData 等函数也感知这些类型:

TypeScript
1data = this.queryClient.getQueryData(this.queries.post(1).queryKey)2// ^? data: Post | undefined

若不使用 queryOptions,data 的类型将为 unknown,除非你显式传入类型参数:

TypeScript
1data = queryClient.getQueryData<Post>(['post', 1])

修改选项(Mutation Options)的类型定义

与 queryOptions 类似,你可使用 mutationOptions 将修改选项提取至独立函数。

TypeScript
1export class QueriesService {2  private queryClient = inject(QueryClient)3 4  updatePost(id: number) {5    return mutationOptions({6      mutationFn: (post: Post) => Promise.resolve(post),7      mutationKey: ['updatePost', id],8      onSuccess: (newPost) => {9        //           ^? newPost: Post10        this.queryClient.setQueryData(['posts', id], newPost)11      },12    })13  }14}

使用 skipToken 实现类型安全的查询禁用

若你使用 TypeScript,可借助 skipToken 禁用查询。当你需要基于条件禁用查询但仍希望保持类型安全时,此功能十分有用。
更多详情请参阅 禁用查询 指南。

本页为 TanStack Query 文档(Angular 版)的非官方中文翻译,原文由上游团队维护,遵循 MIT 许可。 译文如有偏差,以英文原文为准。

译自上游 59d11de