RTK Query的ExtraOptions是否不支持强类型?如何实现端点必填参数校验?
如何让RTK Query端点必填类型化的extraOptions参数
问题背景
需要在定义RTK Query端点时,强制每个端点都提供requiredPermission字符串参数。当前实现中,ExtraOptions必须与{}组成联合类型才能兼容createApi和injectEndpoints,导致类型检查失效——比如拼写错误无法在编译阶段发现,只能靠运行时抛错,希望找到编译时校验的解决方案。
可行解决方案
方案1:自定义严格的createApi类型
通过扩展RTK的类型系统,强制所有端点的extraOptions必须包含requiredPermission。
- 定义严格的ExtraOptions类型
type StrictExtraOptions = { requiredPermission: string; };
- 重写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>;
- 修改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
相关产品推荐
相关产品推荐

