diff --git a/src/apiTypes.ts b/src/apiTypes.ts index 9285b541d..8dbb8db85 100644 --- a/src/apiTypes.ts +++ b/src/apiTypes.ts @@ -2,7 +2,7 @@ * Note: this file should import all other files for type discovery and declaration merging */ import { PatchQueryResultThunk, QueryApi, UpdateQueryResultThunk } from './buildThunks'; -import { AnyAction, Middleware, Reducer, ThunkDispatch } from '@reduxjs/toolkit'; +import { AnyAction, Middleware, Reducer, ThunkDispatch, ThunkAction } from '@reduxjs/toolkit'; import { PrefetchOptions } from './buildHooks'; import { EndpointDefinitions, @@ -12,10 +12,10 @@ import { MutationDefinition, } from './endpointDefinitions'; import { CombinedState, QueryKeys, QueryStatePhantomType, RootState } from './apiState'; -import { InternalActions } from './index'; import { UnionToIntersection } from './tsHelpers'; import { TS41Hooks } from './ts41Types'; import './buildSelectors'; +import { SliceActions } from './buildSlice'; type UnwrapPromise = T extends PromiseLike ? V : T; type MaybePromise = T | PromiseLike; @@ -110,3 +110,7 @@ export type ApiWithInjectedEndpoints< Omit & { endpoints: ApiDefinition['endpoints'] & Partial>; }; + +export type InternalActions = SliceActions & { + prefetchThunk: (endpointName: any, arg: any, options: PrefetchOptions) => ThunkAction; +}; diff --git a/src/buildActionMaps.ts b/src/buildActionMaps.ts index 31342b62a..2df4d4b1d 100644 --- a/src/buildActionMaps.ts +++ b/src/buildActionMaps.ts @@ -2,7 +2,7 @@ import { EndpointDefinitions, QueryDefinition, MutationDefinition, QueryArgFrom import type { QueryThunkArg, MutationThunkArg } from './buildThunks'; import { AnyAction, AsyncThunk, ThunkAction } from '@reduxjs/toolkit'; import { MutationSubState, QueryStatus, QuerySubState, SubscriptionOptions } from './apiState'; -import { InternalSerializeQueryArgs } from '.'; +import { InternalSerializeQueryArgs } from './defaultSerializeQueryArgs'; import { Api, ApiEndpointMutation, ApiEndpointQuery } from './apiTypes'; declare module './apiTypes' { diff --git a/src/buildSelectors.ts b/src/buildSelectors.ts index 3a40be644..1083ac3dd 100644 --- a/src/buildSelectors.ts +++ b/src/buildSelectors.ts @@ -16,7 +16,7 @@ import { ReducerPathFrom, } from './endpointDefinitions'; import type { InternalState } from './buildSlice'; -import { InternalSerializeQueryArgs } from '.'; +import { InternalSerializeQueryArgs } from './defaultSerializeQueryArgs'; export const skipSelector = Symbol('skip selector'); diff --git a/src/buildThunks.ts b/src/buildThunks.ts index db91661d3..b04319580 100644 --- a/src/buildThunks.ts +++ b/src/buildThunks.ts @@ -1,4 +1,4 @@ -import { InternalSerializeQueryArgs } from '.'; +import { InternalSerializeQueryArgs } from './defaultSerializeQueryArgs'; import { Api, ApiEndpointQuery, BaseQueryFn, BaseQueryArg, BaseQueryError } from './apiTypes'; import { InternalRootState, QueryKeys, QueryStatus, QuerySubstateIdentifier } from './apiState'; import { StartQueryActionCreatorOptions } from './buildActionMaps'; diff --git a/src/defaultSerializeQueryArgs.ts b/src/defaultSerializeQueryArgs.ts new file mode 100644 index 000000000..31e6c8990 --- /dev/null +++ b/src/defaultSerializeQueryArgs.ts @@ -0,0 +1,18 @@ +import { QueryCacheKey } from './apiState'; + +export const defaultSerializeQueryArgs: SerializeQueryArgs = ({ endpoint, queryArgs }) => { + // Sort the object keys before stringifying, to prevent useQuery({ a: 1, b: 2 }) having a different cache key than useQuery({ b: 2, a: 1 }) + return `${endpoint}(${JSON.stringify(queryArgs, Object.keys(queryArgs || {}).sort())})`; +}; + +export type SerializeQueryArgs = (_: { + queryArgs: any; + internalQueryArgs: InternalQueryArgs; + endpoint: string; +}) => string; + +export type InternalSerializeQueryArgs = (_: { + queryArgs: any; + internalQueryArgs: InternalQueryArgs; + endpoint: string; +}) => QueryCacheKey; diff --git a/src/index.ts b/src/index.ts index a552de7b6..45a449276 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,47 +1,30 @@ -import { buildThunks } from './buildThunks'; -import type { AnyAction, Reducer, ThunkAction } from '@reduxjs/toolkit'; -import { buildSlice, SliceActions } from './buildSlice'; +import type { AnyAction, Reducer } from '@reduxjs/toolkit'; +import type { CombinedState, QueryStatePhantomType } from './apiState'; +import { Api, BaseQueryArg, BaseQueryFn } from './apiTypes'; import { buildActionMaps } from './buildActionMaps'; -import { buildSelectors } from './buildSelectors'; -import { buildHooks, PrefetchOptions } from './buildHooks'; +import { buildHooks } from './buildHooks'; import { buildMiddleware } from './buildMiddleware'; +import { buildSelectors } from './buildSelectors'; +import { buildSlice } from './buildSlice'; +import { buildThunks } from './buildThunks'; +import { defaultSerializeQueryArgs, InternalSerializeQueryArgs, SerializeQueryArgs } from './defaultSerializeQueryArgs'; import { - EndpointDefinitions, - EndpointBuilder, + AssertEntityTypes, DefinitionType, - isQueryDefinition, + EndpointBuilder, + EndpointDefinitions, isMutationDefinition, - AssertEntityTypes, + isQueryDefinition, } from './endpointDefinitions'; -import type { CombinedState, QueryCacheKey, QueryStatePhantomType } from './apiState'; import { assertCast } from './tsHelpers'; -import { Api, BaseQueryArg, BaseQueryFn } from './apiTypes'; -export { Api, ApiWithInjectedEndpoints, BaseQueryFn, BaseQueryEnhancer } from './apiTypes'; -export { fetchBaseQuery, FetchBaseQueryError } from './fetchBaseQuery'; +import { capitalize, IS_DEV } from './utils'; +export { ApiProvider } from './ApiProvider'; export { QueryStatus } from './apiState'; +export type { Api, ApiWithInjectedEndpoints, BaseQueryEnhancer, BaseQueryFn } from './apiTypes'; +export { fetchBaseQuery } from './fetchBaseQuery'; +export type { FetchBaseQueryError } from './fetchBaseQuery'; export { retry } from './retry'; -export { ApiProvider } from './ApiProvider'; - -export type SerializeQueryArgs = (_: { - queryArgs: any; - internalQueryArgs: InternalQueryArgs; - endpoint: string; -}) => string; - -export type InternalSerializeQueryArgs = (_: { - queryArgs: any; - internalQueryArgs: InternalQueryArgs; - endpoint: string; -}) => QueryCacheKey; - -const defaultSerializeQueryArgs: SerializeQueryArgs = ({ endpoint, queryArgs }) => { - // Sort the object keys before stringifying, to prevent useQuery({ a: 1, b: 2 }) having a different cache key than useQuery({ b: 2, a: 1 }) - return `${endpoint}(${JSON.stringify(queryArgs, Object.keys(queryArgs || {}).sort())})`; -}; - -const IS_DEV = () => typeof process !== 'undefined' && process.env.NODE_ENV === 'development'; - export function createApi< BaseQuery extends BaseQueryFn, Definitions extends EndpointDefinitions, @@ -205,11 +188,3 @@ export function createApi< return api.injectEndpoints({ endpoints }); } - -export type InternalActions = SliceActions & { - prefetchThunk: (endpointName: any, arg: any, options: PrefetchOptions) => ThunkAction; -}; - -function capitalize(str: string) { - return str.replace(str[0], str[0].toUpperCase()); -} diff --git a/src/utils/capitalize.ts b/src/utils/capitalize.ts new file mode 100644 index 000000000..b7aa41b38 --- /dev/null +++ b/src/utils/capitalize.ts @@ -0,0 +1,3 @@ +export function capitalize(str: string) { + return str.replace(str[0], str[0].toUpperCase()); +} diff --git a/src/utils/index.ts b/src/utils/index.ts index 343a7f19c..af8b0856b 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -4,3 +4,5 @@ export * from './joinUrls'; export * from './flatten'; export * from './shallowEqual'; export * from './useShallowStableValue'; +export * from './capitalize'; +export * from './isDev'; diff --git a/src/utils/isDev.ts b/src/utils/isDev.ts new file mode 100644 index 000000000..16e49bb89 --- /dev/null +++ b/src/utils/isDev.ts @@ -0,0 +1 @@ +export const IS_DEV = () => typeof process !== 'undefined' && process.env.NODE_ENV === 'development'; diff --git a/test/matchers.test.tsx b/test/matchers.test.tsx index 3d4f48c28..f63e17c88 100644 --- a/test/matchers.test.tsx +++ b/test/matchers.test.tsx @@ -130,6 +130,7 @@ test('inferred types', () => { }) .addMatcher(api.endpoints.querySuccess.matchFulfilled, (state, action) => { expectExactType({} as ResultType)(action.payload.result); + expectExactType(0 as number)(action.payload.fulfilledTimeStamp); // @ts-expect-error console.log(action.error); expectExactType({} as ArgType)(action.meta.arg.originalArgs);