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

RTK Query的ExtraOptions是否不支持强类型?如何实现端点必填参数校验?

如何让RTK Query端点必填类型化的extraOptions参数

问题背景

需要在定义RTK Query端点时,强制每个端点都提供requiredPermission字符串参数。当前实现中,ExtraOptions必须与{}组成联合类型才能兼容createApi和injectEndpoints,导致类型检查失效——比如拼写错误无法在编译阶段发现,只能靠运行时抛错,希望找到编译时校验的解决方案。

可行解决方案

方案1:自定义严格的createApi类型

通过扩展RTK的类型系统,强制所有端点的extraOptions必须包含requiredPermission。

  1. 定义严格的ExtraOptions类型
type StrictExtraOptions = {
  requiredPermission: string;
};
  1. 重写createApi的类型签名,覆盖默认的ExtraOptions约束
import { createApi as originalCreateApi, BaseQueryFn, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
import { QueryReturnValue } from '@reduxjs/toolkit/query/baseQueryTypes';

type StrictBaseQueryFn = BaseQueryFn<string, unknown, unknown, StrictExtraOptions, {}>;

// 自定义createApi,强制baseQuery使用StrictBaseQueryFn
export const createApi = originalCreateApi as <
  Definitions extends Record<string, any>,
  ReducerPath extends string = 'api',
  TagTypes extends string = never,
>(
  args: Omit<Parameters<typeof originalCreateApi>[0], 'baseQuery'> & {
    baseQuery: StrictBaseQueryFn;
  }
) => ReturnType<typeof originalCreateApi>;
  1. 修改baseQuery实现
export function baseQuery(baseURL: string): StrictBaseQueryFn {
  return async (args, api, extraOptions): Promise<QueryReturnValue<unknown, unknown, {}>> => {
    // 类型已确保requiredPermission存在,无需额外判断
    console.log(extraOptions.requiredPermission);
    
    return fetchBaseQuery({ baseURL })(args, api, {});
  };
}

此时使用createApi时,任何端点缺少requiredPermission或参数拼写错误,都会触发编译错误:

createApi({
  baseQuery: baseQuery('/'),
  endpoints: (builder) => ({
    correctEndpoint: builder.query<string, void>({
      query: () => 'a',
      extraOptions: {
        requiredPermission: 'somePerm' // 正常通过校验
      }
    }),
    badEndpointWithTypo: builder.query<string, void>({
      query: () => 'b',
      extraOptions: {
        requiredPermision: 'somePerm' // 编译错误:类型不匹配
      }
    }),
    missingPermission: builder.query<string, void>({
      query: () => 'c' // 编译错误:缺少extraOptions
    })
  })
});

方案2:封装端点构建函数

如果不想修改createApi的类型,可以包装builder.query和builder.mutation,强制传入合规的extraOptions。

import { EndpointBuilder } from '@reduxjs/toolkit/query';

// 定义严格的query/mutation参数类型
type StrictQueryOptions<QueryArg, Result> = Omit<
  Parameters<EndpointBuilder<any, any, any>['query']>[0],
  'extraOptions'
> & {
  extraOptions: { requiredPermission: string };
};

type StrictMutationOptions<QueryArg, Result> = Omit<
  Parameters<EndpointBuilder<any, any, any>['mutation']>[0],
  'extraOptions'
> & {
  extraOptions: { requiredPermission: string };
};

// 包装builder,替换query/mutation方法为严格版本
const wrapBuilder = <B extends EndpointBuilder<any, any, any>>(builder: B) => ({
  ...builder,
  query: ((options: StrictQueryOptions<any, any>) => builder.query(options)) as typeof builder.query,
  mutation: ((options: StrictMutationOptions<any, any>) => builder.mutation(options)) as typeof builder.mutation,
});

使用方式:

createApi({
  baseQuery: baseQuery('/'),
  endpoints: (builder) => {
    const strictBuilder = wrapBuilder(builder);
    return {
      correctEndpoint: strictBuilder.query<string, void>({
        query: () => 'a',
        extraOptions: { requiredPermission: 'somePerm' }
      }),
      badEndpoint: strictBuilder.query<string, void>({
        query: () => 'b',
        extraOptions: { requiredPermision: 'somePerm' } // 编译错误
      })
    };
  }
});

方案3:利用条件类型做强制校验

通过TypeScript条件类型,确保传入的extraOptions必须包含requiredPermission:

import { BaseQueryFn, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
import { QueryReturnValue } from '@reduxjs/toolkit/query/baseQueryTypes';

// 条件类型:仅允许包含requiredPermission的类型
type EnsurePermission<EO> = EO extends { requiredPermission: string } ? EO : never;

export function baseQuery<EO extends EnsurePermission<EO>>(baseURL: string): BaseQueryFn<string, unknown, unknown, EO, {}> {
  return async (args, api, extraOptions): Promise<QueryReturnValue<unknown, unknown, {}>> => {
    console.log(extraOptions.requiredPermission);
    return fetchBaseQuery({ baseURL })(args, api, {});
  };
}

使用时需显式指定泛型:

createApi({
  baseQuery: baseQuery<{ requiredPermission: string }>('/'),
  endpoints: (builder) => ({
    correctEndpoint: builder.query<string, void>({
      query: () => 'a',
      extraOptions: { requiredPermission: 'somePerm' }
    }),
    badEndpoint: builder.query<string, void>({
      query: () => 'b',
      extraOptions: { requiredPermision: 'somePerm' } // 编译错误
    })
  })
});

方案优势

以上三种方案均能在编译阶段检测到requiredPermission的缺失或拼写错误,避免运行时才发现问题,大幅降低端点定义时的出错概率。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 18:35:24