mirror of
https://github.com/gosticks/DefinitelyTyped.git
synced 2026-07-03 16:50:15 +00:00
[react-relay] Improve nullability handling of pagination hooks (#40579)
* Fix nullabily of usePaginationFragment to match that of useFragment * usePaginationFragmanet: add test for the non-null case * Fix nullabily of useBlockingPaginationFragment to match that of usePaginationFragment
This commit is contained in:
committed by
Sheetal Nandi
parent
4d7955b2e9
commit
eaf62c8120
@@ -14,23 +14,30 @@ export interface ReturnType<TQuery extends OperationType, TKey, TFragmentData> {
|
||||
|
||||
export type $Call<Fn extends (...args: any[]) => any> = Fn extends (arg: any) => infer RT ? RT : never;
|
||||
|
||||
export type NonNullableReturnType<T extends { readonly ' $data'?: unknown }> = (arg: T) => NonNullable<T[' $data']>;
|
||||
export type NullableReturnType<T extends { readonly ' $data'?: unknown | null }> = (arg: T) => T[' $data'] | null;
|
||||
interface KeyType {
|
||||
readonly ' $data'?: unknown;
|
||||
}
|
||||
|
||||
type KeyReturnType<T extends KeyType> = (arg: T) => NonNullable<T[' $data']>;
|
||||
|
||||
export function useBlockingPaginationFragment<
|
||||
TQuery extends OperationType,
|
||||
TKey extends { readonly ' $data'?: unknown | null }
|
||||
TKey extends KeyType
|
||||
>(
|
||||
fragmentInput: GraphQLTaggedNode,
|
||||
parentFragmentRef: TKey,
|
||||
componentDisplayName?: string,
|
||||
): ReturnType<
|
||||
// tslint:disable-next-line:no-unnecessary-generics
|
||||
TQuery,
|
||||
TKey,
|
||||
// NOTE: This $Call ensures that the type of the returned data is either:
|
||||
// - nullable if the provided ref type is nullable
|
||||
// - non-nullable if the provided ref type is non-nullable
|
||||
// prettier-ignore
|
||||
$Call<NonNullableReturnType<TKey> & NullableReturnType<TKey>>
|
||||
>;
|
||||
): // tslint:disable-next-line no-unnecessary-generics
|
||||
ReturnType<TQuery, TKey, $Call<KeyReturnType<TKey>>>;
|
||||
|
||||
export function useBlockingPaginationFragment<
|
||||
TQuery extends OperationType,
|
||||
TKey extends KeyType
|
||||
>(
|
||||
fragmentInput: GraphQLTaggedNode,
|
||||
parentFragmentRef: TKey | null,
|
||||
componentDisplayName?: string,
|
||||
): // tslint:disable-next-line no-unnecessary-generics
|
||||
ReturnType<TQuery, TKey | null, $Call<KeyReturnType<TKey>> | null>;
|
||||
|
||||
export { };
|
||||
|
||||
@@ -15,14 +15,28 @@ export interface ReturnType<TQuery extends OperationType, TKey, TFragmentData> {
|
||||
|
||||
export type $Call<Fn extends (...args: any[]) => any> = Fn extends (arg: any) => infer RT ? RT : never;
|
||||
|
||||
export type NonNullableReturnType<T extends { readonly ' $data'?: unknown }> = (arg: T) => NonNullable<T[' $data']>;
|
||||
export type NullableReturnType<T extends { readonly ' $data'?: unknown | null }> = (arg: T) => T[' $data'] | null;
|
||||
interface KeyType {
|
||||
readonly ' $data'?: unknown;
|
||||
}
|
||||
|
||||
type KeyReturnType<T extends KeyType> = (arg: T) => NonNullable<T[' $data']>;
|
||||
|
||||
export function useLegacyPaginationFragment<
|
||||
TQuery extends OperationType,
|
||||
TKey extends { readonly ' $data'?: unknown | null }
|
||||
TKey extends KeyType
|
||||
>(
|
||||
fragmentInput: GraphQLTaggedNode,
|
||||
parentFragmentRef: TKey,
|
||||
): // tslint:disable-next-line no-unnecessary-generics
|
||||
ReturnType<TQuery, TKey, $Call<NonNullableReturnType<TKey> & NullableReturnType<TKey>>>;
|
||||
ReturnType<TQuery, TKey, $Call<KeyReturnType<TKey>>>;
|
||||
|
||||
export function useLegacyPaginationFragment<
|
||||
TQuery extends OperationType,
|
||||
TKey extends KeyType
|
||||
>(
|
||||
fragmentInput: GraphQLTaggedNode,
|
||||
parentFragmentRef: TKey | null,
|
||||
): // tslint:disable-next-line no-unnecessary-generics
|
||||
ReturnType<TQuery, TKey | null, $Call<KeyReturnType<TKey>> | null>;
|
||||
|
||||
export {};
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
useFragment,
|
||||
useRefetchableFragment,
|
||||
usePaginationFragment,
|
||||
useBlockingPaginationFragment,
|
||||
} from 'react-relay/hooks';
|
||||
|
||||
const source = new RecordSource();
|
||||
@@ -431,7 +432,7 @@ function PaginationFragment() {
|
||||
}
|
||||
|
||||
interface Props {
|
||||
user: FriendsListComponent_user$key;
|
||||
user: FriendsListComponent_user$key | null;
|
||||
}
|
||||
|
||||
return function FriendsList(props: Props) {
|
||||
@@ -476,3 +477,249 @@ function PaginationFragment() {
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
function PaginationFragment_WithNonNullUserProp() {
|
||||
interface FriendsListPaginationQueryVariables {
|
||||
count?: number;
|
||||
cursor?: string;
|
||||
id: string;
|
||||
}
|
||||
interface FriendsListPaginationQueryResponse {
|
||||
readonly node: {
|
||||
readonly ' $fragmentRefs': FragmentRefs<'FriendsListComponent_user'>;
|
||||
};
|
||||
}
|
||||
interface FriendsListPaginationQuery {
|
||||
readonly response: FriendsListPaginationQueryResponse;
|
||||
readonly variables: FriendsListPaginationQueryVariables;
|
||||
}
|
||||
|
||||
interface FriendsListComponent_user {
|
||||
readonly name: string;
|
||||
readonly friends: {
|
||||
readonly edges: ReadonlyArray<{
|
||||
readonly node: {
|
||||
readonly name: string;
|
||||
readonly age: number;
|
||||
};
|
||||
}>;
|
||||
};
|
||||
readonly id: string;
|
||||
readonly ' $refType': 'FriendsListComponent_user';
|
||||
}
|
||||
type FriendsListComponent_user$data = FriendsListComponent_user;
|
||||
interface FriendsListComponent_user$key {
|
||||
readonly ' $data'?: FriendsListComponent_user$data;
|
||||
readonly ' $fragmentRefs': FragmentRefs<'FriendsListComponent_user'>;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
user: FriendsListComponent_user$key;
|
||||
}
|
||||
|
||||
return function FriendsList(props: Props) {
|
||||
const {
|
||||
data,
|
||||
loadNext,
|
||||
loadPrevious,
|
||||
hasNext,
|
||||
hasPrevious,
|
||||
isLoadingNext,
|
||||
isLoadingPrevious,
|
||||
refetch, // For refetching connection
|
||||
} = usePaginationFragment<FriendsListPaginationQuery, FriendsListComponent_user$key>(
|
||||
graphql`
|
||||
fragment FriendsListComponent_user on User @refetchable(queryName: "FriendsListPaginationQuery") {
|
||||
name
|
||||
friends(first: $count, after: $cursor) @connection(key: "FriendsList_user_friends") {
|
||||
edges {
|
||||
node {
|
||||
name
|
||||
age
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
props.user,
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<h1>Friends of {data.name}:</h1>
|
||||
|
||||
{data.friends.edges.map(({ node }) => (
|
||||
<div>
|
||||
{node.name} - {node.age}
|
||||
</div>
|
||||
))}
|
||||
|
||||
<button onClick={() => loadNext(10)}>Load more friends</button>
|
||||
</>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests for useBlockingPaginationFragment
|
||||
* see https://relay.dev/docs/en/experimental/api-reference#useblockingpaginationfragment
|
||||
*/
|
||||
function BlockingPaginationFragment() {
|
||||
interface FriendsListPaginationQueryVariables {
|
||||
count?: number;
|
||||
cursor?: string;
|
||||
id: string;
|
||||
}
|
||||
interface FriendsListPaginationQueryResponse {
|
||||
readonly node: {
|
||||
readonly ' $fragmentRefs': FragmentRefs<'FriendsListComponent_user'>;
|
||||
};
|
||||
}
|
||||
interface FriendsListPaginationQuery {
|
||||
readonly response: FriendsListPaginationQueryResponse;
|
||||
readonly variables: FriendsListPaginationQueryVariables;
|
||||
}
|
||||
|
||||
interface FriendsListComponent_user {
|
||||
readonly name: string;
|
||||
readonly friends: {
|
||||
readonly edges: ReadonlyArray<{
|
||||
readonly node: {
|
||||
readonly name: string;
|
||||
readonly age: number;
|
||||
};
|
||||
}>;
|
||||
};
|
||||
readonly id: string;
|
||||
readonly ' $refType': 'FriendsListComponent_user';
|
||||
}
|
||||
type FriendsListComponent_user$data = FriendsListComponent_user;
|
||||
interface FriendsListComponent_user$key {
|
||||
readonly ' $data'?: FriendsListComponent_user$data;
|
||||
readonly ' $fragmentRefs': FragmentRefs<'FriendsListComponent_user'>;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
user: FriendsListComponent_user$key | null;
|
||||
}
|
||||
|
||||
return function FriendsList(props: Props) {
|
||||
const {
|
||||
data,
|
||||
loadNext,
|
||||
loadPrevious,
|
||||
hasNext,
|
||||
hasPrevious,
|
||||
refetch, // For refetching connection
|
||||
} = useBlockingPaginationFragment<FriendsListPaginationQuery, FriendsListComponent_user$key>(
|
||||
graphql`
|
||||
fragment FriendsListComponent_user on User @refetchable(queryName: "FriendsListPaginationQuery") {
|
||||
name
|
||||
friends(first: $count, after: $cursor) @connection(key: "FriendsList_user_friends") {
|
||||
edges {
|
||||
node {
|
||||
name
|
||||
age
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
props.user,
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<h1>Friends of {data!.name}:</h1>
|
||||
|
||||
{data!.friends.edges.map(({ node }) => (
|
||||
<div>
|
||||
{node.name} - {node.age}
|
||||
</div>
|
||||
))}
|
||||
|
||||
<button onClick={() => loadNext(10)}>Load more friends</button>
|
||||
</>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
function BlockingPaginationFragment_WithNonNullUserProp() {
|
||||
interface FriendsListPaginationQueryVariables {
|
||||
count?: number;
|
||||
cursor?: string;
|
||||
id: string;
|
||||
}
|
||||
interface FriendsListPaginationQueryResponse {
|
||||
readonly node: {
|
||||
readonly ' $fragmentRefs': FragmentRefs<'FriendsListComponent_user'>;
|
||||
};
|
||||
}
|
||||
interface FriendsListPaginationQuery {
|
||||
readonly response: FriendsListPaginationQueryResponse;
|
||||
readonly variables: FriendsListPaginationQueryVariables;
|
||||
}
|
||||
|
||||
interface FriendsListComponent_user {
|
||||
readonly name: string;
|
||||
readonly friends: {
|
||||
readonly edges: ReadonlyArray<{
|
||||
readonly node: {
|
||||
readonly name: string;
|
||||
readonly age: number;
|
||||
};
|
||||
}>;
|
||||
};
|
||||
readonly id: string;
|
||||
readonly ' $refType': 'FriendsListComponent_user';
|
||||
}
|
||||
type FriendsListComponent_user$data = FriendsListComponent_user;
|
||||
interface FriendsListComponent_user$key {
|
||||
readonly ' $data'?: FriendsListComponent_user$data;
|
||||
readonly ' $fragmentRefs': FragmentRefs<'FriendsListComponent_user'>;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
user: FriendsListComponent_user$key;
|
||||
}
|
||||
|
||||
return function FriendsList(props: Props) {
|
||||
const {
|
||||
data,
|
||||
loadNext,
|
||||
loadPrevious,
|
||||
hasNext,
|
||||
hasPrevious,
|
||||
refetch, // For refetching connection
|
||||
} = useBlockingPaginationFragment<FriendsListPaginationQuery, FriendsListComponent_user$key>(
|
||||
graphql`
|
||||
fragment FriendsListComponent_user on User @refetchable(queryName: "FriendsListPaginationQuery") {
|
||||
name
|
||||
friends(first: $count, after: $cursor) @connection(key: "FriendsList_user_friends") {
|
||||
edges {
|
||||
node {
|
||||
name
|
||||
age
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
props.user,
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<h1>Friends of {data.name}:</h1>
|
||||
|
||||
{data.friends.edges.map(({ node }) => (
|
||||
<div>
|
||||
{node.name} - {node.age}
|
||||
</div>
|
||||
))}
|
||||
|
||||
<button onClick={() => loadNext(10)}>Load more friends</button>
|
||||
</>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user