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

@reduxjs/toolkit如何生成强类型动态API钩子?

Redux Toolkit Query 钩子类型生成原理解析

以下是使用@reduxjs/toolkit创建API的简单示例:

import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';

const output = createApi( {
    reducerPath: 'my-api',
    keepUnusedDataFor: CACHE_TIMEOUT,
    baseQuery: fetchBaseQuery( {
        baseUrl,
        fetchFn,
        prepareHeaders,
    } ),
    endpoints: ( builder ) => ( {
        foo: builder.query<any, Params>( {
            query: ( params: Params ) => {
                return {...};
            },
        } ),
        edit: builder.mutation<any, Params>( {
            query: ( params: Params ) => {
                return {...};
            }
        } ),
    } ),
} );

在这个示例中,output变量除了其他方法和属性外,还会基于endpoints自动生成3个强类型钩子:

  • output.useFooQuery
  • output.useLazyFooQuery
  • output.useEditMutation

我不清楚它底层是如何生成这些最终输出类型的,希望有人能解释原理并提供演示。我多次尝试阅读源码但进展有限,还没完全放弃,但想直接了解工作原理来节省时间——因为之后我想运用这项技术。感谢帮助。


核心原理:TypeScript 高级类型系统的编译期推导

RTK Query的类型生成完全依赖TypeScript的泛型、条件类型、映射类型组合,没有运行时代码生成,所有逻辑都在编译阶段完成,核心步骤如下:

1. 端点类型的基础绑定

builder.query和builder.mutation是泛型函数,会接收你传入的「返回值类型」和「参数类型」,生成带类型标记的端点定义:

  • builder.query<ResultType, ArgType>:标记该端点为查询类型,绑定返回值ResultType和入参ArgType
  • builder.mutation<ResultType, ArgType>:标记该端点为修改类型,同样绑定对应类型

这些函数会把你的端点配置(比如query函数)和泛型参数结合,生成包含完整类型信息的端点描述对象。

2. 端点类型的统一收集

createApi的泛型会自动解析endpoints回调返回的所有端点,整理成一个「端点类型映射表」。比如示例中的两个端点会被转化为:

type EndpointMap = {
  foo: QueryEndpointDefinition<any, Params>;
  edit: MutationEndpointDefinition<any, Params>;
};

3. 钩子类型的自动生成

RTK Query内部预定义了一套类型工具,会遍历上述映射表,根据端点类型(query/mutation)生成对应钩子:

  • 针对query类型端点:
    • 生成useXxxQuery:自动触发查询的钩子,类型继承端点的参数和返回值类型
    • 生成useLazyXxxQuery:手动触发查询的钩子,类型规则一致
  • 针对mutation类型端点:
    • 生成useXxxMutation:执行修改操作的钩子,绑定对应参数和返回值类型

整个过程都是TypeScript的编译期类型推导,没有额外代码生成,最终output的类型会自动包含这些钩子。


简化演示:手动模拟类型生成逻辑

我们可以用TypeScript实现一个极简版的类型生成逻辑,直观理解这个过程:

// 定义基础端点类型
type QueryEndpoint<Result, Arg> = { type: 'query'; result: Result; arg: Arg };
type MutationEndpoint<Result, Arg> = { type: 'mutation'; result: Result; arg: Arg };

// 模拟builder的query/mutation方法
const mockBuilder = {
  query: <R, A>() => ({ type: 'query' } as QueryEndpoint<R, A>),
  mutation: <R, A>() => ({ type: 'mutation' } as MutationEndpoint<R, A>),
};

// 核心类型工具:生成钩子类型
type GenerateHooks<Endpoints> = {
  // 遍历端点,根据类型生成钩子名称
  [K in keyof Endpoints as 
    Endpoints[K] extends QueryEndpoint<infer R, infer A>
      ? `use${Capitalize<string & K>}Query` | `useLazy${Capitalize<string & K>}Query`
      : Endpoints[K] extends MutationEndpoint<infer R, infer A>
        ? `use${Capitalize<string & K>}Mutation`
        : never
  ]: 
    // 为钩子绑定对应参数和返回值类型
    Endpoints[K] extends QueryEndpoint<infer R, infer A>
      ? (arg: A) => { data: R | undefined }
      : Endpoints[K] extends MutationEndpoint<infer R, infer A>
        ? () => [(arg: A) => Promise<R>, { data: R | undefined }]
        : never
};

// 模拟createApi的返回类型
type MockApiReturn<Endpoints> = GenerateHooks<Endpoints> & {
  reducerPath: string;
};

// 测试示例
type MyEndpoints = {
  foo: QueryEndpoint<any, Params>;
  edit: MutationEndpoint<any, Params>;
};

type MyApi = MockApiReturn<MyEndpoints>;

// 此时MyApi的类型会包含:
// useFooQuery: (arg: Params) => { data: any | undefined }
// useLazyFooQuery: (arg: Params) => { data: any | undefined }
// useEditMutation: () => [(arg: Params) => Promise<any>, { data: any | undefined }]

这个示例模拟了RTK Query的核心逻辑——通过条件类型判断端点类型,映射类型生成钩子名称,类型推断绑定钩子的参数和返回值。

内容的提问来源于stack exchange,提问作者chris.middleton

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 13:53:17