From 1742b2f422182f90ce46b88a1938a237fc1bb4f3 Mon Sep 17 00:00:00 2001 From: Christian Sany Date: Thu, 11 Jun 2026 19:26:01 +0200 Subject: [PATCH 1/4] Add missing TypeScript declarations --- packages/react-relay/hooks.d.ts | 2 + .../relay-hooks/RelayEnvironmentProvider.d.ts | 2 + .../useMutationAction_EXPERIMENTAL.d.ts | 12 ++++ ...PrefetchableForwardPaginationFragment.d.ts | 69 +++++++++++++++++++ 4 files changed, 85 insertions(+) create mode 100644 packages/react-relay/relay-hooks/useMutationAction_EXPERIMENTAL.d.ts create mode 100644 packages/react-relay/relay-hooks/usePrefetchableForwardPaginationFragment.d.ts diff --git a/packages/react-relay/hooks.d.ts b/packages/react-relay/hooks.d.ts index 608f03bb823ab..70a9aa6dc2cd8 100644 --- a/packages/react-relay/hooks.d.ts +++ b/packages/react-relay/hooks.d.ts @@ -80,7 +80,9 @@ export { useEntryPointLoader } from './relay-hooks/useEntryPointLoader'; export { useFragment } from './relay-hooks/useFragment'; export { useLazyLoadQuery } from './relay-hooks/useLazyLoadQuery'; export { useMutation } from './relay-hooks/useMutation'; +export { useMutationAction_EXPERIMENTAL } from './relay-hooks/useMutationAction_EXPERIMENTAL'; export { usePaginationFragment } from './relay-hooks/usePaginationFragment'; +export { usePrefetchableForwardPaginationFragment } from './relay-hooks/usePrefetchableForwardPaginationFragment'; export { usePreloadedQuery } from './relay-hooks/usePreloadedQuery'; export { useQueryLoader } from './relay-hooks/useQueryLoader'; export { useRefetchableFragment } from './relay-hooks/useRefetchableFragment'; diff --git a/packages/react-relay/relay-hooks/RelayEnvironmentProvider.d.ts b/packages/react-relay/relay-hooks/RelayEnvironmentProvider.d.ts index 3b7b34f605227..2a9591532b68d 100644 --- a/packages/react-relay/relay-hooks/RelayEnvironmentProvider.d.ts +++ b/packages/react-relay/relay-hooks/RelayEnvironmentProvider.d.ts @@ -7,10 +7,12 @@ import {ProviderProps, ReactElement, ReactNode} from 'react'; import {IEnvironment, RelayContext} from 'relay-runtime'; +import {ActorIdentifier, IActorEnvironment} from 'relay-runtime/multi-actor-environment/index'; export interface Props { children: ReactNode; environment: IEnvironment; + getEnvironmentForActor?: ((actorIdentifier: ActorIdentifier) => IActorEnvironment) | null | undefined; } export function RelayEnvironmentProvider(props: Props): ReactElement>; diff --git a/packages/react-relay/relay-hooks/useMutationAction_EXPERIMENTAL.d.ts b/packages/react-relay/relay-hooks/useMutationAction_EXPERIMENTAL.d.ts new file mode 100644 index 0000000000000..b5d0e52007a40 --- /dev/null +++ b/packages/react-relay/relay-hooks/useMutationAction_EXPERIMENTAL.d.ts @@ -0,0 +1,12 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import {GraphQLTaggedNode, MutationParameters, VariablesOf} from 'relay-runtime'; + +export function useMutationAction_EXPERIMENTAL( + mutation: GraphQLTaggedNode, +): (variables: VariablesOf) => Promise; diff --git a/packages/react-relay/relay-hooks/usePrefetchableForwardPaginationFragment.d.ts b/packages/react-relay/relay-hooks/usePrefetchableForwardPaginationFragment.d.ts new file mode 100644 index 0000000000000..3439382f0d250 --- /dev/null +++ b/packages/react-relay/relay-hooks/usePrefetchableForwardPaginationFragment.d.ts @@ -0,0 +1,69 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import {KeyType, KeyTypeData, RefetchFnDynamic} from '../ReactRelayTypes'; +import {LoadMoreFn} from './useLoadMoreFunction'; +import {GraphQLTaggedNode, OperationType, VariablesOf} from 'relay-runtime'; + +export interface PrefetchableForwardPaginationReturn< + TQuery extends OperationType, + TKey extends KeyType | null | undefined, + TFragmentData, + TEdgeData, +> { + data: TFragmentData; + loadNext: LoadMoreFn; + hasNext: boolean; + isLoadingNext: boolean; + refetch: RefetchFnDynamic; + edges: TEdgeData; +} + +export type GetExtraVariablesFn = (args: { + hasNext: boolean; + data: TFragmentData; + getServerEdges: () => TEdgeData; +}) => Partial>; + +export interface PrefetchingLoadMoreOptions { + UNSTABLE_extraVariables?: + | Partial> + | GetExtraVariablesFn + | undefined; + onComplete?: ((error: Error | null) => void) | undefined; +} + +export function usePrefetchableForwardPaginationFragment< + TQuery extends OperationType, + TKey extends KeyType, + TEdgeData, +>( + fragmentInput: GraphQLTaggedNode, + parentFragmentRef: TKey, + bufferSize: number, + initialSize?: number | null | undefined, + prefetchingLoadMoreOptions?: PrefetchingLoadMoreOptions, TEdgeData>, + minimalFetchSize?: number, + disablePrefetching?: boolean, +): PrefetchableForwardPaginationReturn, TEdgeData>; +export function usePrefetchableForwardPaginationFragment< + TQuery extends OperationType, + TKey extends KeyType, + TEdgeData, +>( + fragmentInput: GraphQLTaggedNode, + parentFragmentRef: TKey | null | undefined, + bufferSize: number, + initialSize?: number | null | undefined, + prefetchingLoadMoreOptions?: PrefetchingLoadMoreOptions< + TQuery, + KeyTypeData | null | undefined, + TEdgeData + >, + minimalFetchSize?: number, + disablePrefetching?: boolean, +): PrefetchableForwardPaginationReturn | null | undefined, TEdgeData>; From 8ff517f3c625746866bdec39d2dff6e1c9e18c77 Mon Sep 17 00:00:00 2001 From: Christian Sany Date: Wed, 24 Jun 2026 11:19:33 +0200 Subject: [PATCH 2/4] Refactor useMutation and useMutationAction_EXPERIMENTAL to use OperationType instead of MutationParameters for better type safety --- .../react-relay/relay-hooks/useMutation.d.ts | 24 +++++++++++++++---- .../useMutationAction_EXPERIMENTAL.d.ts | 8 +++++-- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/packages/react-relay/relay-hooks/useMutation.d.ts b/packages/react-relay/relay-hooks/useMutation.d.ts index 79ad37c940f8a..9a5e564ea35eb 100644 --- a/packages/react-relay/relay-hooks/useMutation.d.ts +++ b/packages/react-relay/relay-hooks/useMutation.d.ts @@ -11,14 +11,30 @@ import { GraphQLTaggedNode, IEnvironment, MutationConfig, - MutationParameters, + OperationType, PayloadError, SelectorStoreUpdater, UploadableMap, VariablesOf, } from 'relay-runtime'; -export interface UseMutationConfig { +// `useMutation` is constrained to `OperationType` (`response: unknown`) rather +// than `MutationParameters` (`response: Record`): the Flow +// source places no constraint on the response (`TData`), and the stricter bag +// rejects `@catch` mutations whose `response` is a `Result<…>` union / an +// interface-typed object that lacks an implicit index signature. +// +// Relay's `MutationConfig` (used for the optional `commitMutationFn` override) +// still requires `MutationParameters`, so the response/rawResponse are re-mapped +// into an index-signature-compatible shape only where it is referenced. +type IndexSignatureCompatible = T extends unknown ? { [Key in keyof T]: T[Key] } : never; +type AsMutationParameters = { + response: IndexSignatureCompatible; + variables: TMutation['variables']; + rawResponse?: IndexSignatureCompatible; +}; + +export interface UseMutationConfig { variables: VariablesOf; updater?: SelectorStoreUpdater | null | undefined; uploadables?: UploadableMap | undefined; @@ -30,7 +46,7 @@ export interface UseMutationConfig { onUnsubscribe?: (() => void | null) | undefined; } -export function useMutation( +export function useMutation( mutation: GraphQLTaggedNode, - commitMutationFn?: (environment: IEnvironment, config: MutationConfig) => Disposable, + commitMutationFn?: (environment: IEnvironment, config: MutationConfig>) => Disposable, ): [(config: UseMutationConfig) => Disposable, boolean]; diff --git a/packages/react-relay/relay-hooks/useMutationAction_EXPERIMENTAL.d.ts b/packages/react-relay/relay-hooks/useMutationAction_EXPERIMENTAL.d.ts index b5d0e52007a40..725532c049cd9 100644 --- a/packages/react-relay/relay-hooks/useMutationAction_EXPERIMENTAL.d.ts +++ b/packages/react-relay/relay-hooks/useMutationAction_EXPERIMENTAL.d.ts @@ -5,8 +5,12 @@ * LICENSE file in the root directory of this source tree. */ -import {GraphQLTaggedNode, MutationParameters, VariablesOf} from 'relay-runtime'; +import {GraphQLTaggedNode, OperationType, VariablesOf} from 'relay-runtime'; -export function useMutationAction_EXPERIMENTAL( +// Constrained to `OperationType` (response: unknown), not `MutationParameters` +// (response: Record): the Flow source places no constraint on +// the response/`TData`, and the stricter bag rejects `@catch` mutations whose +// response is a `Result<…>` union. This also matches the other modern hooks. +export function useMutationAction_EXPERIMENTAL( mutation: GraphQLTaggedNode, ): (variables: VariablesOf) => Promise; From de9a01d6758eb67929be8f624d7110563aa8771e Mon Sep 17 00:00:00 2001 From: Christian Sany Date: Thu, 25 Jun 2026 12:21:06 +0200 Subject: [PATCH 3/4] Follow relays naming conventions --- .../usePrefetchableForwardPaginationFragment.d.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/react-relay/relay-hooks/usePrefetchableForwardPaginationFragment.d.ts b/packages/react-relay/relay-hooks/usePrefetchableForwardPaginationFragment.d.ts index 3439382f0d250..ae5b7be2f57c3 100644 --- a/packages/react-relay/relay-hooks/usePrefetchableForwardPaginationFragment.d.ts +++ b/packages/react-relay/relay-hooks/usePrefetchableForwardPaginationFragment.d.ts @@ -9,7 +9,7 @@ import {KeyType, KeyTypeData, RefetchFnDynamic} from '../ReactRelayTypes'; import {LoadMoreFn} from './useLoadMoreFunction'; import {GraphQLTaggedNode, OperationType, VariablesOf} from 'relay-runtime'; -export interface PrefetchableForwardPaginationReturn< +export interface usePrefetchableForwardPaginationFragmentHookType< TQuery extends OperationType, TKey extends KeyType | null | undefined, TFragmentData, @@ -49,7 +49,7 @@ export function usePrefetchableForwardPaginationFragment< prefetchingLoadMoreOptions?: PrefetchingLoadMoreOptions, TEdgeData>, minimalFetchSize?: number, disablePrefetching?: boolean, -): PrefetchableForwardPaginationReturn, TEdgeData>; +): usePrefetchableForwardPaginationFragmentHookType, TEdgeData>; export function usePrefetchableForwardPaginationFragment< TQuery extends OperationType, TKey extends KeyType, @@ -66,4 +66,4 @@ export function usePrefetchableForwardPaginationFragment< >, minimalFetchSize?: number, disablePrefetching?: boolean, -): PrefetchableForwardPaginationReturn | null | undefined, TEdgeData>; +): usePrefetchableForwardPaginationFragmentHookType | null | undefined, TEdgeData>; From dd2c7b65eefb95920d08cf6894e584e75b05cc3e Mon Sep 17 00:00:00 2001 From: Christian Sany Date: Thu, 25 Jun 2026 12:30:44 +0200 Subject: [PATCH 4/4] Fix the MutationParameters type to have an easier fix for mutations types --- .../react-relay/relay-hooks/useMutation.d.ts | 24 ++++--------------- .../mutations/commitMutation.d.ts | 13 ++++++---- 2 files changed, 13 insertions(+), 24 deletions(-) diff --git a/packages/react-relay/relay-hooks/useMutation.d.ts b/packages/react-relay/relay-hooks/useMutation.d.ts index 9a5e564ea35eb..79ad37c940f8a 100644 --- a/packages/react-relay/relay-hooks/useMutation.d.ts +++ b/packages/react-relay/relay-hooks/useMutation.d.ts @@ -11,30 +11,14 @@ import { GraphQLTaggedNode, IEnvironment, MutationConfig, - OperationType, + MutationParameters, PayloadError, SelectorStoreUpdater, UploadableMap, VariablesOf, } from 'relay-runtime'; -// `useMutation` is constrained to `OperationType` (`response: unknown`) rather -// than `MutationParameters` (`response: Record`): the Flow -// source places no constraint on the response (`TData`), and the stricter bag -// rejects `@catch` mutations whose `response` is a `Result<…>` union / an -// interface-typed object that lacks an implicit index signature. -// -// Relay's `MutationConfig` (used for the optional `commitMutationFn` override) -// still requires `MutationParameters`, so the response/rawResponse are re-mapped -// into an index-signature-compatible shape only where it is referenced. -type IndexSignatureCompatible = T extends unknown ? { [Key in keyof T]: T[Key] } : never; -type AsMutationParameters = { - response: IndexSignatureCompatible; - variables: TMutation['variables']; - rawResponse?: IndexSignatureCompatible; -}; - -export interface UseMutationConfig { +export interface UseMutationConfig { variables: VariablesOf; updater?: SelectorStoreUpdater | null | undefined; uploadables?: UploadableMap | undefined; @@ -46,7 +30,7 @@ export interface UseMutationConfig { onUnsubscribe?: (() => void | null) | undefined; } -export function useMutation( +export function useMutation( mutation: GraphQLTaggedNode, - commitMutationFn?: (environment: IEnvironment, config: MutationConfig>) => Disposable, + commitMutationFn?: (environment: IEnvironment, config: MutationConfig) => Disposable, ): [(config: UseMutationConfig) => Disposable, boolean]; diff --git a/packages/relay-runtime/mutations/commitMutation.d.ts b/packages/relay-runtime/mutations/commitMutation.d.ts index c61d5e12c98ad..5a82995257449 100644 --- a/packages/relay-runtime/mutations/commitMutation.d.ts +++ b/packages/relay-runtime/mutations/commitMutation.d.ts @@ -8,13 +8,18 @@ import {PayloadError, UploadableMap} from '../network/RelayNetworkTypes'; import { GraphQLTaggedNode } from '../query/GraphQLTag'; import {Environment, SelectorStoreUpdater} from '../store/RelayStoreTypes'; -import {CacheConfig, Disposable} from '../util/RelayRuntimeTypes'; +import {CacheConfig, Disposable, Variables} from '../util/RelayRuntimeTypes'; import { DeclarativeMutationConfig } from './RelayDeclarativeMutationConfig'; +// Mirrors the Flow type `{response: {...}, variables: {...}, rawResponse?: {...}}`, +// where `{...}` is Flow's inexact empty object ("any object"). `object` is the +// faithful TS equivalent; `Record` would be too strict — it +// requires an implicit index signature, which `@catch` mutations (whose response +// is a `Result<…>` union) and interface-typed responses do not have. export interface MutationParameters { - readonly response: Record; - readonly variables: Record; - readonly rawResponse?: Record | undefined; + readonly response: object; + readonly variables: Variables; + readonly rawResponse?: object | undefined; } export interface MutationConfig {