You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用代码分割时如何生成RTK-Query端点的正确Typescript类型

动态注入端点的TypeScript类型与预取钩子实现方案

方案1:全局类型扩充实现统一预取钩子

这个方案适合需要全局统一的usePrefetch钩子,支持所有已注入端点的场景:

  1. 首先在空API定义文件中预留可扩充的端点类型接口:
// emptySplitApi.ts
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'

export const emptySplitApi = createApi({
  baseQuery: fetchBaseQuery({ baseUrl: '/' }),
  endpoints: () => ({}),
})

// 声明可扩充的端点名称接口
export interface SplitApiEndpointNames {}
export type AllEndpointNames = SplitApiEndpointNames[keyof SplitApiEndpointNames]
  1. 每个调用injectEndpoints的模块同步扩充该接口:
// userApi.ts
import { emptySplitApi, type SplitApiEndpointNames } from './emptySplitApi'

const extendedApi = emptySplitApi.injectEndpoints({
  endpoints: (build) => ({
    getUserInfo: build.query<User, number>({ query: (id) => `user/${id}` }),
    listUserOrders: build.query<Order[], number>({ query: (userId) => `user/${userId}/orders` }),
  }),
})

// 扩充全局端点类型
declare module './emptySplitApi' {
  interface SplitApiEndpointNames {
    getUserInfo: 'getUserInfo'
    listUserOrders: 'listUserOrders'
  }
}

export const { useGetUserInfoQuery, useListUserOrdersQuery } = extendedApi
  1. 按照官方示例实现带类型的预取钩子即可:
import { useDispatch } from 'react-redux'
import { useCallback } from 'react'
import { emptySplitApi, type AllEndpointNames } from './emptySplitApi'
import type { PrefetchOptions } from '@reduxjs/toolkit/query'

export const usePrefetch = <T extends AllEndpointNames>(endpointName: T, options?: PrefetchOptions) => {
  const dispatch = useDispatch()
  return useCallback(
    (arg: Parameters<typeof emptySplitApi.endpoints[T]['initiate']>[0], prefetchOptions?: PrefetchOptions) => {
      dispatch(emptySplitApi.util.prefetch(endpointName, arg, { ...options, ...prefetchOptions }))
    },
    [dispatch, endpointName, options]
  )
}

这个方案的usePrefetch会自动识别所有已注入的端点名称,以及对应端点的参数类型。


方案2:分模块导出独立预取钩子

这个方案适合完全隔离的代码分割场景,不需要全局类型扩充,每个模块的预取钩子仅支持当前模块注入的端点:
每个injectEndpoints的模块直接基于扩展后的API实例生成专属预取钩子:

// userApi.ts
import { emptySplitApi } from './emptySplitApi'
import { useDispatch } from 'react-redux'
import { useCallback } from 'react'
import type { PrefetchOptions } from '@reduxjs/toolkit/query'

const userApi = emptySplitApi.injectEndpoints({
  endpoints: (build) => ({
    getUserInfo: build.query<User, number>({ query: (id) => `user/${id}` }),
    listUserOrders: build.query<Order[], number>({ query: (userId) => `user/${userId}/orders` }),
  }),
})

// 生成仅支持当前模块端点的预取钩子
type UserEndpointNames = keyof typeof userApi.endpoints
export const useUserPrefetch = <T extends UserEndpointNames>(endpointName: T, options?: PrefetchOptions) => {
  const dispatch = useDispatch()
  return useCallback(
    (arg: Parameters<typeof userApi.endpoints[T]['initiate']>[0], prefetchOptions?: PrefetchOptions) => {
      dispatch(userApi.util.prefetch(endpointName, arg, { ...options, ...prefetchOptions }))
    },
    [dispatch, endpointName, options]
  )
}

export const { useGetUserInfoQuery, useListUserOrdersQuery } = userApi

这个方案的优势是完全不需要维护全局类型,模块之间互不干扰,也不会出现调用了尚未注入的端点的类型提示。


注意事项

  • 采用方案1时要保证类型扩充的模块路径完全匹配,否则类型不会正常合并
  • 懒加载端点的场景优先选择方案2,类型安全性更高

内容的提问来源于stack exchange,提问作者Jake Ols

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.29 03:54:04