Skip to content

Commit 17b491e

Browse files
authored
Merge pull request DefinitelyTyped#28379 from alloy/relay-queries-and-mutations
[react-relay] Add generics to QueryRenderer and commitMutation.
2 parents 5280761 + 55b8085 commit 17b491e

3 files changed

Lines changed: 89 additions & 39 deletions

File tree

types/react-relay/index.d.ts

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
// Cameron Knight <https://github.com/ckknight>
88
// Kaare Hoff Skovgaard <https://github.com/kastermester>
99
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
10-
// TypeScript Version: 2.8
10+
// TypeScript Version: 2.9
1111

1212
// Prettified with:
1313
// $ prettier --parser typescript --tab-width 4 --semi --trailing-comma es5 --write --print-width 120 \
@@ -88,24 +88,27 @@ export const graphql: GraphqlInterface;
8888
// ~~~~~~~~~~~~~~~~~~~~~
8989
// ReactRelayQueryRenderer
9090
// ~~~~~~~~~~~~~~~~~~~~~
91-
export interface QueryRendererProps {
91+
92+
export interface QueryRendererProps<T extends RelayRuntimeTypes.OperationBase = RelayRuntimeTypes.OperationDefaults> {
9293
cacheConfig?: RelayRuntimeTypes.CacheConfig;
9394
environment: RelayRuntimeTypes.Environment;
9495
query?: RelayRuntimeTypes.GraphQLTaggedNode | null;
95-
render(readyState: ReadyState): React.ReactElement<any> | undefined | null;
96-
variables: RelayRuntimeTypes.Variables;
96+
render(readyState: ReadyState<T["response"]>): React.ReactElement<any> | undefined | null;
97+
variables: T["variables"];
9798
rerunParamExperimental?: RelayRuntimeTypes.RerunParam;
9899
}
99-
export interface ReadyState {
100+
export interface ReadyState<T extends RelayRuntimeTypes.Variables = RelayRuntimeTypes.Variables> {
100101
error: Error | undefined | null;
101-
props: { [propName: string]: any } | undefined | null;
102+
props: T | undefined | null;
102103
retry?(): void;
103104
}
104-
export interface QueryRendererState {
105-
readyState: ReadyState;
106-
}
107-
export class ReactRelayQueryRenderer extends React.Component<QueryRendererProps, QueryRendererState> {}
108-
export class QueryRenderer extends ReactRelayQueryRenderer {}
105+
106+
export class ReactRelayQueryRenderer<T extends RelayRuntimeTypes.OperationBase> extends React.Component<
107+
QueryRendererProps<T>
108+
> {}
109+
export class QueryRenderer<
110+
T extends RelayRuntimeTypes.OperationBase = RelayRuntimeTypes.OperationDefaults
111+
> extends ReactRelayQueryRenderer<T> {}
109112

110113
// ~~~~~~~~~~~~~~~~~~~~~
111114
// createFragmentContainer

types/react-relay/test/react-relay-tests.tsx

Lines changed: 49 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
// tslint:disable:interface-over-type-literal
2+
13
import * as React from "react";
24
import { Environment, Network, RecordSource, Store, ConnectionHandler, FragmentReference } from "relay-runtime";
35

@@ -28,8 +30,21 @@ const modernEnvironment = new Environment({ network, store });
2830
// ~~~~~~~~~~~~~~~~~~~~~
2931
// Modern QueryRenderer
3032
// ~~~~~~~~~~~~~~~~~~~~~
33+
34+
// Artifact produced by relay-compiler-language-typescript
35+
type MyQueryRendererVariables = {
36+
pageID: string;
37+
};
38+
type MyQueryRendererResponse = {
39+
name: string;
40+
};
41+
type MyQueryRenderer = {
42+
variables: MyQueryRendererVariables;
43+
response: MyQueryRendererResponse;
44+
};
45+
3146
const MyQueryRenderer = (props: { name: string; show: boolean }) => (
32-
<QueryRenderer
47+
<QueryRenderer<MyQueryRenderer>
3348
environment={modernEnvironment}
3449
query={
3550
props.show
@@ -82,7 +97,6 @@ type StoryLike = (storyID: string) => void;
8297
// Artifact produced by relay-compiler-language-typescript
8398
declare const _Story_story$ref: unique symbol;
8499
type Story_story$ref = typeof _Story_story$ref;
85-
// tslint:disable-next-line:interface-over-type-literal
86100
type Story_story = {
87101
readonly id: string;
88102
readonly text: string;
@@ -173,7 +187,6 @@ declare const _FeedStories_feed$ref: unique symbol;
173187
type FeedStories_feed$ref = typeof _FeedStories_feed$ref;
174188
declare const _FeedStory_edges$ref: unique symbol;
175189
type FeedStory_edges$ref = typeof _FeedStory_edges$ref;
176-
// tslint:disable-next-line:interface-over-type-literal
177190
type FeedStories_feed = {
178191
readonly edges: ReadonlyArray<{
179192
readonly node: {
@@ -184,7 +197,6 @@ type FeedStories_feed = {
184197
}>;
185198
readonly " $refType": FeedStories_feed$ref;
186199
};
187-
// tslint:disable-next-line:interface-over-type-literal
188200
type FeedStory_edges = ReadonlyArray<{
189201
readonly publishedAt: string;
190202
readonly " $refType": FeedStory_edges$ref;
@@ -255,7 +267,6 @@ const Feed = (() => {
255267
// Artifact produced by relay-compiler-language-typescript
256268
declare const _UserFeed_user$ref: unique symbol;
257269
type UserFeed_user$ref = typeof _UserFeed_user$ref;
258-
// tslint:disable-next-line:interface-over-type-literal
259270
type UserFeed_user = {
260271
readonly feed: {
261272
readonly pageInfo: {
@@ -372,7 +383,7 @@ export const mutation = graphql`
372383
export const optimisticResponse = {
373384
markReadNotification: {
374385
notification: {
375-
seenState: "SEEN",
386+
seenState: "SEEN" as "SEEN",
376387
},
377388
},
378389
};
@@ -408,26 +419,47 @@ export const configs = [
408419
];
409420

410421
function markNotificationAsRead(source: string, storyID: string) {
411-
const variables = {
412-
input: {
413-
source,
414-
storyID,
415-
},
422+
// Artifact produced by relay-compiler-language-typescript
423+
type MyMutationVariables = {
424+
readonly input: {
425+
readonly source: string;
426+
readonly storyID: string;
427+
};
428+
};
429+
type MyMutationResponse = {
430+
readonly markReadNotification: {
431+
readonly notification: {
432+
readonly seenState: "SEEN" | "UNSEEN";
433+
};
434+
};
435+
};
436+
type MyMutation = {
437+
readonly variables: MyMutationVariables;
438+
readonly response: MyMutationResponse;
416439
};
417440

418-
commitMutation(modernEnvironment, {
441+
commitMutation<MyMutation>(modernEnvironment, {
419442
configs,
420443
mutation,
421444
optimisticResponse,
422-
variables,
445+
variables: {
446+
input: {
447+
source,
448+
storyID,
449+
},
450+
},
423451
onCompleted: (response, errors) => {
424-
console.log("Response received from server.");
452+
if (errors) {
453+
console.log(`Errors received from server: ${errors.map(error => error.message).join(", ")}`);
454+
} else {
455+
console.log(`Response received from server: ${response.markReadNotification.notification.seenState}`);
456+
}
425457
},
426458
onError: err => console.error(err),
427459
updater: (store, data) => {
428-
const field = store.get(storyID);
429-
if (field) {
430-
field.setValue(data.story, "story");
460+
const story = store.get(storyID);
461+
if (story) {
462+
story.setValue(data.markReadNotification.notification.seenState, "seenState");
431463
}
432464
},
433465
});

types/relay-runtime/index.d.ts

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
// Definitions by: Matt Martin <https://github.com/voxmatt>
44
// Eloy Durán <https://github.com/alloy>
55
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
6-
// TypeScript Version: 2.4
6+
// TypeScript Version: 2.9
77

88
// Prettified with:
99
// $ prettier --parser typescript --tab-width 4 --semi --trailing-comma es5 --write --print-width 120 \
@@ -51,6 +51,15 @@ export type RequestNode = ConcreteRequest | ConcreteBatchRequest;
5151
// tslint:disable-next-line:no-const-enum
5252
export const enum FragmentReference {}
5353

54+
export interface OperationBase {
55+
variables: object;
56+
response: object;
57+
}
58+
export interface OperationDefaults {
59+
variables: Variables;
60+
response: Variables;
61+
}
62+
5463
// ~~~~~~~~~~~~~~~~~~~~~
5564
// RelayQL
5665
// ~~~~~~~~~~~~~~~~~~~~~
@@ -140,11 +149,11 @@ export type StoreUpdater = (store: RecordSourceProxy) => void;
140149
* order to easily access the root fields of a query/mutation as well as a
141150
* second argument of the response object of the mutation.
142151
*/
143-
export type SelectorStoreUpdater = (
152+
export type SelectorStoreUpdater<T = any> = (
144153
store: RecordSourceSelectorProxy,
145154
// Actually RelayCombinedEnvironmentTypes#SelectorData, but mixed is
146155
// inconvenient to access deeply in product code.
147-
data: any // FLOW FIXME
156+
data: T
148157
) => void;
149158

150159
/**
@@ -483,7 +492,9 @@ export interface CUnstableEnvironmentCore<TEnvironment, TFragment, TGraphQLTagge
483492
operationVariables: Variables,
484493
fragments: CFragmentMap<TFragment>,
485494
props: Props
486-
): { [key: string]: CSelector<TNode> | Array<CSelector<TNode>> | null | undefined };
495+
): {
496+
[key: string]: CSelector<TNode> | Array<CSelector<TNode>> | null | undefined;
497+
};
487498

488499
/**
489500
* Given a mapping of keys -> results and a mapping of keys -> fragments,
@@ -1005,18 +1016,22 @@ export type commitLocalUpdate = (environment: Environment, updater: StoreUpdater
10051016
// commitRelayModernMutation
10061017
// ~~~~~~~~~~~~~~~~~~~~~
10071018
// exposed through RelayModern, not Runtime directly
1008-
export interface MutationConfig<T> {
1019+
export interface MutationConfig<T extends OperationBase> {
10091020
configs?: RelayMutationConfig[];
10101021
mutation: GraphQLTaggedNode;
1011-
variables: Variables;
1022+
variables: T["variables"];
10121023
uploadables?: UploadableMap;
1013-
onCompleted?(response: T, errors: PayloadError[] | null | undefined): void;
1024+
onCompleted?(response: T["response"], errors: PayloadError[] | null | undefined): void;
10141025
onError?(error?: Error): void;
1015-
optimisticUpdater?: SelectorStoreUpdater;
1016-
optimisticResponse?: object;
1017-
updater?: SelectorStoreUpdater;
1026+
optimisticUpdater?: SelectorStoreUpdater<T["response"]>;
1027+
optimisticResponse?: T["response"];
1028+
updater?: SelectorStoreUpdater<T["response"]>;
10181029
}
1019-
export function commitRelayModernMutation(environment: Environment, config: MutationConfig<any>): Disposable;
1030+
export function commitRelayModernMutation<T extends OperationBase = OperationDefaults>(
1031+
environment: Environment,
1032+
// tslint:disable-next-line:no-unnecessary-generics
1033+
config: MutationConfig<T>
1034+
): Disposable;
10201035

10211036
// ~~~~~~~~~~~~~~~~~~~~~
10221037
// applyRelayModernOptimisticMutation

0 commit comments

Comments
 (0)