Skip to content

Commit

Permalink
types: add OmitKeyof to Omit object by object's keys strictly (#7139)
Browse files Browse the repository at this point in the history
* feat(*): add OmitKeyOf to validate Omitting keys

* Update packages/vue-query/src/__tests__/useQueries.types.test.ts

* Update packages/vue-query/src/__tests__/useQueries.types.test.ts

* chore(*): update OmitKeyof's type

Co-authored-by: 2-NOW <kj109888@gmail.com>

* test(query-core): add test case for OmitKeyof

* chore: update

---------

Co-authored-by: 2-NOW <kj109888@gmail.com>
  • Loading branch information
manudeli and 2-NOW committed Mar 22, 2024
1 parent 2ddf803 commit 39b2f81
Show file tree
Hide file tree
Showing 32 changed files with 180 additions and 69 deletions.
3 changes: 2 additions & 1 deletion packages/angular-query-experimental/src/inject-queries.ts
Expand Up @@ -5,6 +5,7 @@ import { injectQueryClient } from './inject-query-client'
import type { Injector, Signal } from '@angular/core'
import type {
DefaultError,
OmitKeyof,
QueriesObserverOptions,
QueriesPlaceholderDataFunction,
QueryFunction,
Expand All @@ -22,7 +23,7 @@ type QueryObserverOptionsForCreateQueries<
TError = DefaultError,
TData = TQueryFnData,
TQueryKey extends QueryKey = QueryKey,
> = Omit<
> = OmitKeyof<
QueryObserverOptions<TQueryFnData, TError, TData, TQueryFnData, TQueryKey>,
'placeholderData'
> & {
Expand Down
11 changes: 6 additions & 5 deletions packages/angular-query-experimental/src/types.ts
Expand Up @@ -9,6 +9,7 @@ import type {
MutateFunction,
MutationObserverOptions,
MutationObserverResult,
OmitKeyof,
QueryKey,
QueryObserverOptions,
QueryObserverResult,
Expand All @@ -34,7 +35,7 @@ export interface CreateQueryOptions<
TError = DefaultError,
TData = TQueryFnData,
TQueryKey extends QueryKey = QueryKey,
> extends Omit<
> extends OmitKeyof<
CreateBaseQueryOptions<
TQueryFnData,
TError,
Expand Down Expand Up @@ -82,7 +83,7 @@ export interface CreateInfiniteQueryOptions<
TQueryData = TQueryFnData,
TQueryKey extends QueryKey = QueryKey,
TPageParam = unknown,
> extends Omit<
> extends OmitKeyof<
InfiniteQueryObserverOptions<
TQueryFnData,
TError,
Expand All @@ -99,7 +100,7 @@ export type CreateBaseQueryResult<
TError = DefaultError,
TState = QueryObserverResult<TData, TError>,
> = BaseQueryNarrowing<TData, TError> &
MapToSignals<Omit<TState, keyof BaseQueryNarrowing>>
MapToSignals<OmitKeyof<TState, keyof BaseQueryNarrowing, 'safely'>>

export type CreateQueryResult<
TData = unknown,
Expand Down Expand Up @@ -131,7 +132,7 @@ export interface CreateMutationOptions<
TError = DefaultError,
TVariables = void,
TContext = unknown,
> extends Omit<
> extends OmitKeyof<
MutationObserverOptions<TData, TError, TVariables, TContext>,
'_defaulted'
> {}
Expand Down Expand Up @@ -250,7 +251,7 @@ export type CreateMutationResult<
TContext
>,
> = BaseMutationNarrowing<TData, TError, TVariables, TContext> &
MapToSignals<Omit<TState, keyof BaseMutationNarrowing>>
MapToSignals<OmitKeyof<TState, keyof BaseMutationNarrowing, 'safely'>>

type Override<TTargetA, TTargetB> = {
[AKey in keyof TTargetA]: AKey extends keyof TTargetB
Expand Down
59 changes: 59 additions & 0 deletions packages/query-core/src/__tests__/OmitKeyof.test-d.ts
@@ -0,0 +1,59 @@
import { describe, expectTypeOf, it } from 'vitest'
import type { OmitKeyof } from '..'

describe('OmitKeyof', () => {
it("'s type check", () => {
type A = {
x: string
y: number
}

type ExpectedType = {
x: string
}

// Bad point
// 1. original Omit can use 'z' as type parameter with no type error
// 2. original Omit have no auto complete for 2nd type parameter
expectTypeOf<Omit<A, 'z' | 'y'>>().toEqualTypeOf<ExpectedType>()

// Solution

// 1. strictly
expectTypeOf<
OmitKeyof<
A,
// OmitKeyof can't use 'z' as type parameter with type error because A don't have key 'z'
// @ts-expect-error Type does not satisfy the constraint keyof A
'z' | 'y'
>
>().toEqualTypeOf<ExpectedType>
expectTypeOf<
OmitKeyof<
A,
// OmitKeyof can't use 'z' as type parameter with type error because A don't have key 'z'
// @ts-expect-error Type does not satisfy the constraint keyof A
'z' | 'y',
'strictly'
>
>().toEqualTypeOf<ExpectedType>

// 2. safely
expectTypeOf<
OmitKeyof<
A,
// OmitKeyof can't use 'z' as type parameter type error with strictly parameter or default parameter
// @ts-expect-error Type does not satisfy the constraint keyof A
'z' | 'y'
>
>().toEqualTypeOf<ExpectedType>
expectTypeOf<
OmitKeyof<
A,
// With 'safely', OmitKeyof can use 'z' as type parameter like original Omit but This support autocomplete too yet for DX.
'z' | 'y',
'safely'
>
>().toEqualTypeOf<ExpectedType>
})
})
3 changes: 2 additions & 1 deletion packages/query-core/src/infiniteQueryBehavior.ts
Expand Up @@ -3,6 +3,7 @@ import type { QueryBehavior } from './query'
import type {
InfiniteData,
InfiniteQueryPageParamsOptions,
OmitKeyof,
QueryFunctionContext,
QueryKey,
} from './types'
Expand Down Expand Up @@ -67,7 +68,7 @@ export function infiniteQueryBehavior<TQueryFnData, TError, TData, TPageParam>(
return Promise.resolve(data)
}

const queryFnContext: Omit<
const queryFnContext: OmitKeyof<
QueryFunctionContext<QueryKey, unknown>,
'signal'
> = {
Expand Down
8 changes: 6 additions & 2 deletions packages/query-core/src/query.ts
Expand Up @@ -7,6 +7,7 @@ import type {
DefaultError,
FetchStatus,
InitialDataFunction,
OmitKeyof,
QueryFunctionContext,
QueryKey,
QueryMeta,
Expand Down Expand Up @@ -370,7 +371,10 @@ export class Query<
const abortController = new AbortController()

// Create query function context
const queryFnContext: Omit<QueryFunctionContext<TQueryKey>, 'signal'> = {
const queryFnContext: OmitKeyof<
QueryFunctionContext<TQueryKey>,
'signal'
> = {
queryKey: this.queryKey,
meta: this.meta,
}
Expand Down Expand Up @@ -421,7 +425,7 @@ export class Query<
}

// Trigger behavior hook
const context: Omit<
const context: OmitKeyof<
FetchContext<TQueryFnData, TError, TData, TQueryKey>,
'signal'
> = {
Expand Down
15 changes: 9 additions & 6 deletions packages/query-core/src/queryClient.ts
Expand Up @@ -12,7 +12,7 @@ import { focusManager } from './focusManager'
import { onlineManager } from './onlineManager'
import { notifyManager } from './notifyManager'
import { infiniteQueryBehavior } from './infiniteQueryBehavior'
import type { DataTag, NoInfer } from './types'
import type { DataTag, NoInfer, OmitKeyof } from './types'
import type { QueryState } from './query'
import type {
CancelOptions,
Expand Down Expand Up @@ -43,7 +43,7 @@ import type { MutationFilters, QueryFilters, Updater } from './utils'

interface QueryDefaults {
queryKey: QueryKey
defaultOptions: Omit<QueryOptions<any, any, any>, 'queryKey'>
defaultOptions: OmitKeyof<QueryOptions<any, any, any>, 'queryKey'>
}

interface MutationDefaults {
Expand Down Expand Up @@ -429,7 +429,7 @@ export class QueryClient {
setQueryDefaults(
queryKey: QueryKey,
options: Partial<
Omit<QueryObserverOptions<unknown, any, any, any>, 'queryKey'>
OmitKeyof<QueryObserverOptions<unknown, any, any, any>, 'queryKey'>
>,
): void {
this.#queryDefaults.set(hashKey(queryKey), {
Expand All @@ -440,10 +440,10 @@ export class QueryClient {

getQueryDefaults(
queryKey: QueryKey,
): Omit<QueryObserverOptions<any, any, any, any, any>, 'queryKey'> {
): OmitKeyof<QueryObserverOptions<any, any, any, any, any>, 'queryKey'> {
const defaults = [...this.#queryDefaults.values()]

let result: Omit<
let result: OmitKeyof<
QueryObserverOptions<any, any, any, any, any>,
'queryKey'
> = {}
Expand All @@ -458,7 +458,10 @@ export class QueryClient {

setMutationDefaults(
mutationKey: MutationKey,
options: Omit<MutationObserverOptions<any, any, any, any>, 'mutationKey'>,
options: OmitKeyof<
MutationObserverOptions<any, any, any, any>,
'mutationKey'
>,
): void {
this.#mutationDefaults.set(hashKey(mutationKey), {
mutationKey,
Expand Down
15 changes: 13 additions & 2 deletions packages/query-core/src/types.ts
Expand Up @@ -7,6 +7,14 @@ import type { QueryFilters, QueryTypeFilter, SkipToken } from './utils'
import type { QueryCache } from './queryCache'
import type { MutationCache } from './mutationCache'

export type OmitKeyof<
TObject,
TKey extends TStrictly extends 'safely'
? keyof TObject | (string & Record<never, never>)
: keyof TObject,
TStrictly extends 'strictly' | 'safely' = 'strictly',
> = Omit<TObject, TKey>

export type NoInfer<T> = [T][T extends any ? 0 : never]

export interface Register {
Expand Down Expand Up @@ -341,7 +349,7 @@ export type Optional<TTarget, TKey extends keyof TTarget> = Pick<
Partial<TTarget>,
TKey
> &
Omit<TTarget, TKey>
OmitKeyof<TTarget, TKey>

export type DefaultedQueryObserverOptions<
TQueryFnData = unknown,
Expand Down Expand Up @@ -889,7 +897,10 @@ export interface QueryClientConfig {
}

export interface DefaultOptions<TError = DefaultError> {
queries?: Omit<QueryObserverOptions<unknown, TError>, 'suspense' | 'queryKey'>
queries?: OmitKeyof<
QueryObserverOptions<unknown, TError>,
'suspense' | 'queryKey'
>
mutations?: MutationObserverOptions<unknown, TError, unknown, unknown>
}

Expand Down
Expand Up @@ -7,10 +7,10 @@ import {
} from '@tanstack/query-persist-client-core'
import { IsRestoringProvider, QueryClientProvider } from '@tanstack/react-query'
import type { PersistQueryClientOptions } from '@tanstack/query-persist-client-core'
import type { QueryClientProviderProps } from '@tanstack/react-query'
import type { OmitKeyof, QueryClientProviderProps } from '@tanstack/react-query'

export type PersistQueryClientProviderProps = QueryClientProviderProps & {
persistOptions: Omit<PersistQueryClientOptions, 'queryClient'>
persistOptions: OmitKeyof<PersistQueryClientOptions, 'queryClient'>
onSuccess?: () => Promise<unknown> | unknown
}

Expand Down
8 changes: 6 additions & 2 deletions packages/react-query/src/HydrationBoundary.tsx
Expand Up @@ -6,13 +6,17 @@ import { useQueryClient } from './QueryClientProvider'
import type {
DehydratedState,
HydrateOptions,
OmitKeyof,
QueryClient,
} from '@tanstack/query-core'

export interface HydrationBoundaryProps {
state?: unknown
options?: Omit<HydrateOptions, 'defaultOptions'> & {
defaultOptions?: Omit<HydrateOptions['defaultOptions'], 'mutations'>
options?: OmitKeyof<HydrateOptions, 'defaultOptions'> & {
defaultOptions?: OmitKeyof<
Exclude<HydrateOptions['defaultOptions'], undefined>,
'mutations'
>
}
children?: React.ReactNode
queryClient?: QueryClient
Expand Down
3 changes: 2 additions & 1 deletion packages/react-query/src/__tests__/useQueries.test-d.tsx
@@ -1,6 +1,7 @@
import { describe, expectTypeOf, it } from 'vitest'
import { queryOptions } from '../queryOptions'
import { useQueries } from '../useQueries'
import type { OmitKeyof } from '..'
import type { UseQueryOptions } from '../types'

describe('UseQueries config object overload', () => {
Expand Down Expand Up @@ -104,7 +105,7 @@ describe('UseQueries config object overload', () => {
type Data = string

const useCustomQueries = (
options?: Omit<UseQueryOptions<Data>, 'queryKey' | 'queryFn'>,
options?: OmitKeyof<UseQueryOptions<Data>, 'queryKey' | 'queryFn'>,
) => {
return useQueries({
queries: [
Expand Down
3 changes: 2 additions & 1 deletion packages/react-query/src/__tests__/useQuery.test-d.tsx
@@ -1,6 +1,7 @@
import { describe, expectTypeOf, it } from 'vitest'
import { useQuery } from '../useQuery'
import { queryOptions } from '../queryOptions'
import type { OmitKeyof } from '..'
import type { UseQueryOptions } from '../types'

describe('initialData', () => {
Expand Down Expand Up @@ -111,7 +112,7 @@ describe('initialData', () => {
type Data = string

const useCustomQuery = (
options?: Omit<UseQueryOptions<Data>, 'queryKey' | 'queryFn'>,
options?: OmitKeyof<UseQueryOptions<Data>, 'queryKey' | 'queryFn'>,
) => {
return useQuery({
...options,
Expand Down
5 changes: 3 additions & 2 deletions packages/react-query/src/__tests__/useQuery.test.tsx
Expand Up @@ -16,6 +16,7 @@ import {
} from './utils'
import type {
DefinedUseQueryResult,
OmitKeyof,
QueryFunction,
UseQueryOptions,
UseQueryResult,
Expand Down Expand Up @@ -147,7 +148,7 @@ describe('useQuery', () => {
token: string,
// return type must be wrapped with TQueryFnReturn
) => Promise<TQueryFnData>,
options?: Omit<
options?: OmitKeyof<
UseQueryOptions<TQueryFnData, TError, TData, TQueryKey>,
'queryKey' | 'queryFn' | 'initialData'
>,
Expand All @@ -169,7 +170,7 @@ describe('useQuery', () => {
>(
qk: TQueryKey,
fetcher: () => Promise<TQueryFnData>,
options?: Omit<
options?: OmitKeyof<
UseQueryOptions<TQueryFnData, TError, TData, TQueryKey>,
'queryKey' | 'queryFn' | 'initialData'
>,
Expand Down

0 comments on commit 39b2f81

Please sign in to comment.