import type { UseQueryStateOptions } from '@internal/query/react/buildHooks' import type { SerializedError } from '@reduxjs/toolkit' import type { FetchBaseQueryError, QueryDefinition, TypedUseMutationResult, TypedUseQueryHookResult, TypedUseQueryState, TypedUseQueryStateResult, TypedUseQuerySubscriptionResult, TypedLazyQueryTrigger, TypedUseLazyQueryStateResult, TypedUseLazyQuery, TypedUseLazyQuerySubscription, TypedUseMutation, TypedMutationTrigger, TypedUseQuerySubscription, TypedUseQuery, TypedUseQueryStateOptions, } from '@reduxjs/toolkit/query/react' import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react' const baseQuery = fetchBaseQuery() const api = createApi({ baseQuery, endpoints: (build) => ({ getTest: build.query({ query: () => '' }), mutation: build.mutation({ query: () => '' }), }), }) describe('union types', () => { test('query selector union', () => { const result = api.endpoints.getTest.select()({} as any) if (result.isUninitialized) { expectTypeOf(result.data).toBeUndefined() expectTypeOf(result.error).toBeUndefined() expectTypeOf(result.isLoading).toEqualTypeOf() expectTypeOf(result.isError).toEqualTypeOf() expectTypeOf(result.isSuccess).toEqualTypeOf() } if (result.isLoading) { expectTypeOf(result.data).toEqualTypeOf() expectTypeOf(result.error).toEqualTypeOf< SerializedError | FetchBaseQueryError | undefined >() expectTypeOf(result.isUninitialized).toEqualTypeOf() expectTypeOf(result.isError).toEqualTypeOf() expectTypeOf(result.isSuccess).toEqualTypeOf() } if (result.isError) { expectTypeOf(result.data).toEqualTypeOf() expectTypeOf(result.error).toEqualTypeOf< SerializedError | FetchBaseQueryError >() expectTypeOf(result.isUninitialized).toEqualTypeOf() expectTypeOf(result.isLoading).toEqualTypeOf() expectTypeOf(result.isSuccess).toEqualTypeOf() } if (result.isSuccess) { expectTypeOf(result.data).toBeString() expectTypeOf(result.error).toBeUndefined() expectTypeOf(result.isUninitialized).toEqualTypeOf() expectTypeOf(result.isLoading).toEqualTypeOf() expectTypeOf(result.isError).toEqualTypeOf() } expectTypeOf(result).not.toBeNever() // is always one of those four if ( !result.isUninitialized && !result.isLoading && !result.isError && !result.isSuccess ) { expectTypeOf(result).toBeNever() } }) test('useQuery union', () => { const result = api.endpoints.getTest.useQuery() if (result.isUninitialized) { expectTypeOf(result.data).toBeUndefined() expectTypeOf(result.error).toBeUndefined() expectTypeOf(result.isLoading).toEqualTypeOf() expectTypeOf(result.isError).toEqualTypeOf() expectTypeOf(result.isSuccess).toEqualTypeOf() expectTypeOf(result.isFetching).toEqualTypeOf() } if (result.isLoading) { expectTypeOf(result.data).toBeUndefined() expectTypeOf(result.error).toEqualTypeOf< SerializedError | FetchBaseQueryError | undefined >() expectTypeOf(result.isUninitialized).toEqualTypeOf() expectTypeOf(result.isError).toEqualTypeOf() expectTypeOf(result.isSuccess).toEqualTypeOf() expectTypeOf(result.isFetching).toBeBoolean() } if (result.isError) { expectTypeOf(result.data).toEqualTypeOf() expectTypeOf(result.error).toEqualTypeOf< SerializedError | FetchBaseQueryError >() expectTypeOf(result.isUninitialized).toEqualTypeOf() expectTypeOf(result.isLoading).toEqualTypeOf() expectTypeOf(result.isSuccess).toEqualTypeOf() expectTypeOf(result.isFetching).toEqualTypeOf() } if (result.isSuccess) { expectTypeOf(result.data).toBeString() expectTypeOf(result.error).toBeUndefined() expectTypeOf(result.isUninitialized).toEqualTypeOf() expectTypeOf(result.isLoading).toEqualTypeOf() expectTypeOf(result.isError).toEqualTypeOf() expectTypeOf(result.isFetching).toBeBoolean() } if (result.isFetching) { expectTypeOf(result.data).toEqualTypeOf() expectTypeOf(result.error).toEqualTypeOf< SerializedError | FetchBaseQueryError | undefined >() expectTypeOf(result.isUninitialized).toEqualTypeOf() expectTypeOf(result.isLoading).toBeBoolean() expectTypeOf(result.isSuccess).toBeBoolean() expectTypeOf(result.isError).toEqualTypeOf() } expectTypeOf(result.currentData).toEqualTypeOf() if (result.isSuccess) { if (!result.isFetching) { expectTypeOf(result.currentData).toBeString() } else { expectTypeOf(result.currentData).toEqualTypeOf() } } expectTypeOf(result).not.toBeNever() // is always one of those four if ( !result.isUninitialized && !result.isLoading && !result.isError && !result.isSuccess ) { expectTypeOf(result).toBeNever() } }) test('useQuery TS4.1 union', () => { const result = api.useGetTestQuery() if (result.isUninitialized) { expectTypeOf(result.data).toBeUndefined() expectTypeOf(result.error).toBeUndefined() expectTypeOf(result.isLoading).toEqualTypeOf() expectTypeOf(result.isError).toEqualTypeOf() expectTypeOf(result.isSuccess).toEqualTypeOf() expectTypeOf(result.isFetching).toEqualTypeOf() } if (result.isLoading) { expectTypeOf(result.data).toBeUndefined() expectTypeOf(result.error).toEqualTypeOf< SerializedError | FetchBaseQueryError | undefined >() expectTypeOf(result.isUninitialized).toEqualTypeOf() expectTypeOf(result.isError).toEqualTypeOf() expectTypeOf(result.isSuccess).toEqualTypeOf() expectTypeOf(result.isFetching).toBeBoolean() } if (result.isError) { expectTypeOf(result.data).toEqualTypeOf() expectTypeOf(result.error).toEqualTypeOf< SerializedError | FetchBaseQueryError >() expectTypeOf(result.isUninitialized).toEqualTypeOf() expectTypeOf(result.isLoading).toEqualTypeOf() expectTypeOf(result.isSuccess).toEqualTypeOf() expectTypeOf(result.isFetching).toEqualTypeOf() } if (result.isSuccess) { expectTypeOf(result.data).toBeString() expectTypeOf(result.error).toBeUndefined() expectTypeOf(result.isUninitialized).toEqualTypeOf() expectTypeOf(result.isLoading).toEqualTypeOf() expectTypeOf(result.isError).toEqualTypeOf() expectTypeOf(result.isFetching).toBeBoolean() } if (result.isFetching) { expectTypeOf(result.data).toEqualTypeOf() expectTypeOf(result.error).toEqualTypeOf< SerializedError | FetchBaseQueryError | undefined >() expectTypeOf(result.isUninitialized).toEqualTypeOf() expectTypeOf(result.isLoading).toBeBoolean() expectTypeOf(result.isSuccess).toBeBoolean() expectTypeOf(result.isError).toEqualTypeOf() } expectTypeOf(result).not.toBeNever() // is always one of those four if ( !result.isUninitialized && !result.isLoading && !result.isError && !result.isSuccess ) { expectTypeOf(result).toBeNever() } }) test('useLazyQuery union', () => { const [_trigger, result] = api.endpoints.getTest.useLazyQuery() if (result.isUninitialized) { expectTypeOf(result.data).toBeUndefined() expectTypeOf(result.error).toBeUndefined() expectTypeOf(result.isLoading).toEqualTypeOf() expectTypeOf(result.isError).toEqualTypeOf() expectTypeOf(result.isSuccess).toEqualTypeOf() expectTypeOf(result.isFetching).toEqualTypeOf() } if (result.isLoading) { expectTypeOf(result.data).toBeUndefined() expectTypeOf(result.error).toEqualTypeOf< SerializedError | FetchBaseQueryError | undefined >() expectTypeOf(result.isUninitialized).toEqualTypeOf() expectTypeOf(result.isError).toEqualTypeOf() expectTypeOf(result.isSuccess).toEqualTypeOf() expectTypeOf(result.isFetching).toBeBoolean() } if (result.isError) { expectTypeOf(result.data).toEqualTypeOf() expectTypeOf(result.error).toEqualTypeOf< SerializedError | FetchBaseQueryError >() expectTypeOf(result.isUninitialized).toEqualTypeOf() expectTypeOf(result.isLoading).toEqualTypeOf() expectTypeOf(result.isSuccess).toEqualTypeOf() expectTypeOf(result.isFetching).toEqualTypeOf() } if (result.isSuccess) { expectTypeOf(result.data).toBeString() expectTypeOf(result.error).toBeUndefined() expectTypeOf(result.isUninitialized).toEqualTypeOf() expectTypeOf(result.isLoading).toEqualTypeOf() expectTypeOf(result.isError).toEqualTypeOf() expectTypeOf(result.isFetching).toBeBoolean() } if (result.isFetching) { expectTypeOf(result.data).toEqualTypeOf() expectTypeOf(result.error).toEqualTypeOf< SerializedError | FetchBaseQueryError | undefined >() expectTypeOf(result.isUninitialized).toEqualTypeOf() expectTypeOf(result.isLoading).toBeBoolean() expectTypeOf(result.isSuccess).toBeBoolean() expectTypeOf(result.isError).toEqualTypeOf() } expectTypeOf(result).not.toBeNever() // is always one of those four if ( !result.isUninitialized && !result.isLoading && !result.isError && !result.isSuccess ) { expectTypeOf(result).toBeNever() } }) test('useLazyQuery TS4.1 union', () => { const [_trigger, result] = api.useLazyGetTestQuery() if (result.isUninitialized) { expectTypeOf(result.data).toBeUndefined() expectTypeOf(result.error).toBeUndefined() expectTypeOf(result.isLoading).toEqualTypeOf() expectTypeOf(result.isError).toEqualTypeOf() expectTypeOf(result.isSuccess).toEqualTypeOf() expectTypeOf(result.isFetching).toEqualTypeOf() } if (result.isLoading) { expectTypeOf(result.data).toBeUndefined() expectTypeOf(result.error).toEqualTypeOf< SerializedError | FetchBaseQueryError | undefined >() expectTypeOf(result.isUninitialized).toEqualTypeOf() expectTypeOf(result.isError).toEqualTypeOf() expectTypeOf(result.isSuccess).toEqualTypeOf() expectTypeOf(result.isFetching).toBeBoolean() } if (result.isError) { expectTypeOf(result.data).toEqualTypeOf() expectTypeOf(result.error).toEqualTypeOf< SerializedError | FetchBaseQueryError >() expectTypeOf(result.isUninitialized).toEqualTypeOf() expectTypeOf(result.isLoading).toEqualTypeOf() expectTypeOf(result.isSuccess).toEqualTypeOf() expectTypeOf(result.isFetching).toEqualTypeOf() } if (result.isSuccess) { expectTypeOf(result.data).toBeString() expectTypeOf(result.error).toBeUndefined() expectTypeOf(result.isUninitialized).toEqualTypeOf() expectTypeOf(result.isLoading).toEqualTypeOf() expectTypeOf(result.isError).toEqualTypeOf() expectTypeOf(result.isFetching).toBeBoolean() } if (result.isFetching) { expectTypeOf(result.data).toEqualTypeOf() expectTypeOf(result.error).toEqualTypeOf< SerializedError | FetchBaseQueryError | undefined >() expectTypeOf(result.isUninitialized).toEqualTypeOf() expectTypeOf(result.isLoading).toBeBoolean() expectTypeOf(result.isSuccess).toBeBoolean() expectTypeOf(result.isError).toEqualTypeOf() } expectTypeOf(result).not.toBeNever() // is always one of those four if ( !result.isUninitialized && !result.isLoading && !result.isError && !result.isSuccess ) { expectTypeOf(result).toBeNever() } }) test('queryHookResult (without selector) union', async () => { const useQueryStateResult = api.endpoints.getTest.useQueryState() const useQueryResult = api.endpoints.getTest.useQuery() const useQueryStateWithSelectFromResult = api.endpoints.getTest.useQueryState(undefined, { selectFromResult: () => ({ x: true }), }) const { refetch, ...useQueryResultWithoutMethods } = useQueryResult assertType(useQueryStateResult) expectTypeOf(useQueryStateResult).toMatchTypeOf( useQueryResultWithoutMethods, ) expectTypeOf(useQueryStateWithSelectFromResult) .parameter(0) .not.toMatchTypeOf(useQueryResultWithoutMethods) expectTypeOf(api.endpoints.getTest.select).returns.returns.toEqualTypeOf< Awaited> >() }) test('useQueryState (with selectFromResult)', () => { const result = api.endpoints.getTest.useQueryState(undefined, { selectFromResult({ data, isLoading, isFetching, isError, isSuccess, isUninitialized, }) { return { data: data ?? 1, isLoading, isFetching, isError, isSuccess, isUninitialized, } }, }) expectTypeOf({ data: '' as string | number, isUninitialized: false, isLoading: true, isFetching: true, isSuccess: false, isError: false, }).toEqualTypeOf(result) }) test('useQuery (with selectFromResult)', async () => { const { refetch, ...result } = api.endpoints.getTest.useQuery(undefined, { selectFromResult({ data, isLoading, isFetching, isError, isSuccess, isUninitialized, }) { return { data: data ?? 1, isLoading, isFetching, isError, isSuccess, isUninitialized, } }, }) expectTypeOf({ data: '' as string | number, isUninitialized: false, isLoading: true, isFetching: true, isSuccess: false, isError: false, }).toEqualTypeOf(result) expectTypeOf(api.endpoints.getTest.select).returns.returns.toEqualTypeOf< Awaited> >() }) test('useMutation union', () => { const [_trigger, result] = api.endpoints.mutation.useMutation() if (result.isUninitialized) { expectTypeOf(result.data).toBeUndefined() expectTypeOf(result.error).toBeUndefined() expectTypeOf(result.isLoading).toEqualTypeOf() expectTypeOf(result.isError).toEqualTypeOf() expectTypeOf(result.isSuccess).toEqualTypeOf() } if (result.isLoading) { expectTypeOf(result.data).toBeUndefined() expectTypeOf(result.error).toEqualTypeOf< SerializedError | FetchBaseQueryError | undefined >() expectTypeOf(result.isUninitialized).toEqualTypeOf() expectTypeOf(result.isError).toEqualTypeOf() expectTypeOf(result.isSuccess).toEqualTypeOf() } if (result.isError) { expectTypeOf(result.data).toEqualTypeOf() expectTypeOf(result.error).toEqualTypeOf< SerializedError | FetchBaseQueryError >() expectTypeOf(result.isUninitialized).toEqualTypeOf() expectTypeOf(result.isLoading).toEqualTypeOf() expectTypeOf(result.isSuccess).toEqualTypeOf() } if (result.isSuccess) { expectTypeOf(result.data).toBeString() expectTypeOf(result.error).toBeUndefined() expectTypeOf(result.isUninitialized).toEqualTypeOf() expectTypeOf(result.isLoading).toEqualTypeOf() expectTypeOf(result.isError).toEqualTypeOf() } expectTypeOf(result).not.toBeNever() // is always one of those four if ( !result.isUninitialized && !result.isLoading && !result.isError && !result.isSuccess ) { expectTypeOf(result).toBeNever() } }) test('useMutation (with selectFromResult)', () => { const [_trigger, result] = api.endpoints.mutation.useMutation({ selectFromResult({ data, isLoading, isError, isSuccess, isUninitialized, }) { return { data: data ?? 'hi', isLoading, isError, isSuccess, isUninitialized, } }, }) expectTypeOf({ data: '' as string, isUninitialized: false, isLoading: true, isSuccess: false, isError: false, reset: () => {}, }).toMatchTypeOf(result) }) test('useMutation TS4.1 union', () => { const [_trigger, result] = api.useMutationMutation() if (result.isUninitialized) { expectTypeOf(result.data).toBeUndefined() expectTypeOf(result.error).toBeUndefined() expectTypeOf(result.isLoading).toEqualTypeOf() expectTypeOf(result.isError).toEqualTypeOf() expectTypeOf(result.isSuccess).toEqualTypeOf() } if (result.isLoading) { expectTypeOf(result.data).toBeUndefined() expectTypeOf(result.error).toEqualTypeOf< SerializedError | FetchBaseQueryError | undefined >() expectTypeOf(result.isUninitialized).toEqualTypeOf() expectTypeOf(result.isError).toEqualTypeOf() expectTypeOf(result.isSuccess).toEqualTypeOf() } if (result.isError) { expectTypeOf(result.data).toEqualTypeOf() expectTypeOf(result.error).toEqualTypeOf< SerializedError | FetchBaseQueryError >() expectTypeOf(result.isUninitialized).toEqualTypeOf() expectTypeOf(result.isLoading).toEqualTypeOf() expectTypeOf(result.isSuccess).toEqualTypeOf() } if (result.isSuccess) { expectTypeOf(result.data).toBeString() expectTypeOf(result.error).toBeUndefined() expectTypeOf(result.isUninitialized).toEqualTypeOf() expectTypeOf(result.isLoading).toEqualTypeOf() expectTypeOf(result.isError).toEqualTypeOf() } expectTypeOf(result).not.toBeNever() // is always one of those four if ( !result.isUninitialized && !result.isLoading && !result.isError && !result.isSuccess ) { expectTypeOf(result).toBeNever() } }) }) describe('"Typed" helper types', () => { test('useQuery', () => { expectTypeOf>().toMatchTypeOf( api.endpoints.getTest.useQuery, ) const result = api.endpoints.getTest.useQuery() expectTypeOf< TypedUseQueryHookResult >().toEqualTypeOf(result) }) test('useQuery with selectFromResult', () => { const result = api.endpoints.getTest.useQuery(undefined, { selectFromResult: () => ({ x: true }), }) expectTypeOf< TypedUseQueryHookResult >().toEqualTypeOf(result) }) test('useQueryState', () => { expectTypeOf< TypedUseQueryState >().toMatchTypeOf(api.endpoints.getTest.useQueryState) const result = api.endpoints.getTest.useQueryState() expectTypeOf< TypedUseQueryStateResult >().toEqualTypeOf(result) }) test('useQueryState with selectFromResult', () => { const result = api.endpoints.getTest.useQueryState(undefined, { selectFromResult: () => ({ x: true }), }) expectTypeOf< TypedUseQueryStateResult >().toEqualTypeOf(result) }) test('useQueryState options', () => { expectTypeOf< TypedUseQueryStateOptions >().toMatchTypeOf< Parameters[1] >() expectTypeOf< UseQueryStateOptions< QueryDefinition, { x: boolean } > >().toEqualTypeOf< TypedUseQueryStateOptions >() }) test('useQuerySubscription', () => { expectTypeOf< TypedUseQuerySubscription >().toMatchTypeOf(api.endpoints.getTest.useQuerySubscription) const result = api.endpoints.getTest.useQuerySubscription() expectTypeOf< TypedUseQuerySubscriptionResult >().toEqualTypeOf(result) }) test('useLazyQuery', () => { expectTypeOf< TypedUseLazyQuery >().toMatchTypeOf(api.endpoints.getTest.useLazyQuery) const [trigger, result] = api.endpoints.getTest.useLazyQuery() expectTypeOf< TypedLazyQueryTrigger >().toMatchTypeOf(trigger) expectTypeOf< TypedUseLazyQueryStateResult >().toMatchTypeOf(result) }) test('useLazyQuery with selectFromResult', () => { const [trigger, result] = api.endpoints.getTest.useLazyQuery({ selectFromResult: () => ({ x: true }), }) expectTypeOf< TypedLazyQueryTrigger >().toMatchTypeOf(trigger) expectTypeOf< TypedUseLazyQueryStateResult< string, void, typeof baseQuery, { x: boolean } > >().toMatchTypeOf(result) }) test('useLazyQuerySubscription', () => { expectTypeOf< TypedUseLazyQuerySubscription >().toMatchTypeOf(api.endpoints.getTest.useLazyQuerySubscription) const [trigger] = api.endpoints.getTest.useLazyQuerySubscription() expectTypeOf< TypedLazyQueryTrigger >().toMatchTypeOf(trigger) }) test('useMutation', () => { expectTypeOf< TypedUseMutation >().toMatchTypeOf(api.endpoints.mutation.useMutation) const [trigger, result] = api.endpoints.mutation.useMutation() expectTypeOf< TypedMutationTrigger >().toMatchTypeOf(trigger) expectTypeOf< TypedUseMutationResult >().toMatchTypeOf(result) }) test('useQuery - defining selectFromResult separately', () => { const selectFromResult = ( result: TypedUseQueryStateResult, ) => ({ x: true }) const result = api.endpoints.getTest.useQuery(undefined, { selectFromResult, }) expectTypeOf(result).toEqualTypeOf< TypedUseQueryHookResult< string, void, typeof baseQuery, ReturnType > >() }) test('useMutation - defining selectFromResult separately', () => { const selectFromResult = ( result: Omit< TypedUseMutationResult, 'reset' | 'originalArgs' >, ) => ({ x: true }) const [trigger, result] = api.endpoints.mutation.useMutation({ selectFromResult, }) expectTypeOf(result).toEqualTypeOf< TypedUseMutationResult< string, void, typeof baseQuery, ReturnType > >() }) })