diff --git a/packages/react-relay/__tests__/RelayResolverInterface-test.js b/packages/react-relay/__tests__/RelayResolverInterface-test.js
index 342c5c1fed170..61bf5806af0ab 100644
--- a/packages/react-relay/__tests__/RelayResolverInterface-test.js
+++ b/packages/react-relay/__tests__/RelayResolverInterface-test.js
@@ -263,6 +263,45 @@ describe.each([
},
);
+describe.each([
+ {
+ animalType: 'Octopus',
+ numLegs: '8',
+ },
+ {
+ animalType: 'PurpleOctopus',
+ numLegs: '8',
+ },
+])(
+ 'resolvers can return an interface where all implementors are weak model types: %s',
+ ({animalType, numLegs}) => {
+ function WeakAnimalLegsQueryComponent(props: {request: {ofType: string}}) {
+ const data = useClientQuery(
+ graphql`
+ query RelayResolverInterfaceTestWeakAnimalLegsQuery(
+ $request: WeakAnimalRequest!
+ ) {
+ weak_animal(request: $request) {
+ ...RelayResolverInterfaceTestWeakAnimalLegsFragment
+ }
+ }
+ `,
+ {request: props.request},
+ );
+ return ;
+ }
+
+ test(`should read the legs of a ${animalType}`, () => {
+ const animalRenderer = TestRenderer.create(
+
+
+ ,
+ );
+ expect(animalRenderer.toJSON()).toEqual(numLegs);
+ });
+ },
+);
+
test('resolvers can return a list of interfaces where all implementors are strong model types', () => {
function AnimalsLegsQueryComponent(props: {
requests: Array<{ofType: string, returnValidID: boolean}>,
diff --git a/packages/react-relay/__tests__/__generated__/RelayResolverInterfaceTestWeakAnimalLegsQuery.graphql.js b/packages/react-relay/__tests__/__generated__/RelayResolverInterfaceTestWeakAnimalLegsQuery.graphql.js
new file mode 100644
index 0000000000000..cc705d80f2f1b
--- /dev/null
+++ b/packages/react-relay/__tests__/__generated__/RelayResolverInterfaceTestWeakAnimalLegsQuery.graphql.js
@@ -0,0 +1,222 @@
+/**
+ * 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.
+ *
+ * @oncall relay
+ *
+ * @generated SignedSource<<20beace133802bf49b0ab116a3769efa>>
+ * @flow
+ * @lightSyntaxTransform
+ * @nogrep
+ */
+
+/* eslint-disable */
+
+'use strict';
+
+/*::
+import type { ClientRequest, ClientQuery } from 'relay-runtime';
+import type { RelayResolverInterfaceTestWeakAnimalLegsFragment$fragmentType } from "./RelayResolverInterfaceTestWeakAnimalLegsFragment.graphql";
+import {weak_animal as queryWeakAnimalResolverType} from "../../../relay-runtime/store/__tests__/resolvers/WeakAnimalQueryResolvers.js";
+// Type assertion validating that `queryWeakAnimalResolverType` resolver is correctly implemented.
+// A type error here indicates that the type signature of the resolver module is incorrect.
+(queryWeakAnimalResolverType: (
+ args: {|
+ request: WeakAnimalRequest,
+ |},
+) => ?Query__weak_animal$normalization);
+import type { Query__weak_animal$normalization } from "./../../../relay-runtime/store/__tests__/resolvers/__generated__/Query__weak_animal$normalization.graphql";
+export type WeakAnimalRequest = {|
+ ofType: string,
+|};
+export type RelayResolverInterfaceTestWeakAnimalLegsQuery$variables = {|
+ request: WeakAnimalRequest,
+|};
+export type RelayResolverInterfaceTestWeakAnimalLegsQuery$data = {|
+ +weak_animal: ?{|
+ +$fragmentSpreads: RelayResolverInterfaceTestWeakAnimalLegsFragment$fragmentType,
+ |},
+|};
+export type RelayResolverInterfaceTestWeakAnimalLegsQuery = {|
+ response: RelayResolverInterfaceTestWeakAnimalLegsQuery$data,
+ variables: RelayResolverInterfaceTestWeakAnimalLegsQuery$variables,
+|};
+*/
+
+var node/*: ClientRequest*/ = (function(){
+var v0 = [
+ {
+ "defaultValue": null,
+ "kind": "LocalArgument",
+ "name": "request"
+ }
+],
+v1 = [
+ {
+ "kind": "Variable",
+ "name": "request",
+ "variableName": "request"
+ }
+],
+v2 = [
+ {
+ "alias": null,
+ "args": null,
+ "kind": "ScalarField",
+ "name": "__relay_model_instance",
+ "storageKey": null
+ }
+];
+return {
+ "fragment": {
+ "argumentDefinitions": (v0/*: any*/),
+ "kind": "Fragment",
+ "metadata": {
+ "hasClientEdges": true
+ },
+ "name": "RelayResolverInterfaceTestWeakAnimalLegsQuery",
+ "selections": [
+ {
+ "kind": "ClientEdgeToClientObject",
+ "concreteType": null,
+ "modelResolvers": null,
+ "backingField": {
+ "alias": null,
+ "args": (v1/*: any*/),
+ "fragment": null,
+ "kind": "RelayResolver",
+ "name": "weak_animal",
+ "resolverModule": require('./../../../relay-runtime/store/__tests__/resolvers/WeakAnimalQueryResolvers').weak_animal,
+ "path": "weak_animal",
+ "normalizationInfo": {
+ "kind": "OutputType",
+ "concreteType": null,
+ "plural": false,
+ "normalizationNode": require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/Query__weak_animal$normalization.graphql')
+ }
+ },
+ "linkedField": {
+ "alias": null,
+ "args": (v1/*: any*/),
+ "concreteType": null,
+ "kind": "LinkedField",
+ "name": "weak_animal",
+ "plural": false,
+ "selections": [
+ {
+ "args": null,
+ "kind": "FragmentSpread",
+ "name": "RelayResolverInterfaceTestWeakAnimalLegsFragment"
+ }
+ ],
+ "storageKey": null
+ }
+ }
+ ],
+ "type": "Query",
+ "abstractKey": null
+ },
+ "kind": "Request",
+ "operation": {
+ "argumentDefinitions": (v0/*: any*/),
+ "kind": "Operation",
+ "name": "RelayResolverInterfaceTestWeakAnimalLegsQuery",
+ "selections": [
+ {
+ "kind": "ClientEdgeToClientObject",
+ "backingField": {
+ "name": "weak_animal",
+ "args": (v1/*: any*/),
+ "fragment": null,
+ "kind": "RelayResolver",
+ "storageKey": null,
+ "isOutputType": true
+ },
+ "linkedField": {
+ "alias": null,
+ "args": (v1/*: any*/),
+ "concreteType": null,
+ "kind": "LinkedField",
+ "name": "weak_animal",
+ "plural": false,
+ "selections": [
+ {
+ "alias": null,
+ "args": null,
+ "kind": "ScalarField",
+ "name": "__typename",
+ "storageKey": null
+ },
+ {
+ "kind": "InlineFragment",
+ "selections": [
+ {
+ "name": "legs",
+ "args": null,
+ "fragment": {
+ "kind": "InlineFragment",
+ "selections": (v2/*: any*/),
+ "type": "Octopus",
+ "abstractKey": null
+ },
+ "kind": "RelayResolver",
+ "storageKey": null,
+ "isOutputType": true
+ }
+ ],
+ "type": "Octopus",
+ "abstractKey": null
+ },
+ {
+ "kind": "InlineFragment",
+ "selections": [
+ {
+ "name": "legs",
+ "args": null,
+ "fragment": {
+ "kind": "InlineFragment",
+ "selections": (v2/*: any*/),
+ "type": "PurpleOctopus",
+ "abstractKey": null
+ },
+ "kind": "RelayResolver",
+ "storageKey": null,
+ "isOutputType": true
+ }
+ ],
+ "type": "PurpleOctopus",
+ "abstractKey": null
+ }
+ ],
+ "storageKey": null
+ }
+ }
+ ],
+ "clientAbstractTypes": {
+ "__isIWeakAnimal": [
+ "Octopus",
+ "PurpleOctopus"
+ ]
+ }
+ },
+ "params": {
+ "cacheID": "a7c1768c48c387258635d29981a0a126",
+ "id": null,
+ "metadata": {},
+ "name": "RelayResolverInterfaceTestWeakAnimalLegsQuery",
+ "operationKind": "query",
+ "text": null
+ }
+};
+})();
+
+if (__DEV__) {
+ (node/*: any*/).hash = "618354efaad641b1957a94e6cf22400d";
+}
+
+module.exports = ((node/*: any*/)/*: ClientQuery<
+ RelayResolverInterfaceTestWeakAnimalLegsQuery$variables,
+ RelayResolverInterfaceTestWeakAnimalLegsQuery$data,
+>*/);
diff --git a/packages/relay-runtime/store/__tests__/resolvers/WeakAnimalQueryResolvers.js b/packages/relay-runtime/store/__tests__/resolvers/WeakAnimalQueryResolvers.js
new file mode 100644
index 0000000000000..235416fe2e4e5
--- /dev/null
+++ b/packages/relay-runtime/store/__tests__/resolvers/WeakAnimalQueryResolvers.js
@@ -0,0 +1,54 @@
+/**
+ * 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.
+ *
+ * @format
+ * @flow strict-local
+ * @oncall relay
+ */
+
+import type {Query__weak_animal$normalization} from 'relay-runtime/store/__tests__/resolvers/__generated__/Query__weak_animal$normalization.graphql';
+
+/**
+ * Returns a single `IWeakAnimal` of a given type.
+ *
+ * @RelayResolver Query.weak_animal(request: WeakAnimalRequest!): IWeakAnimal
+ */
+function weak_animal(args: {
+ request: {ofType: string},
+}): Query__weak_animal$normalization {
+ switch (args.request.ofType) {
+ case 'Octopus':
+ return {
+ __relay_model_instance: {},
+ __typename: 'Octopus',
+ };
+ case 'PurpleOctopus':
+ return {
+ __relay_model_instance: {},
+ __typename: 'PurpleOctopus',
+ };
+ default:
+ throw new Error('Invalid type');
+ }
+}
+
+/**
+ * Returns a list of `IWeakAnimal` of a given type.
+ *
+ * @RelayResolver Query.weak_animals(requests: [WeakAnimalRequest!]!): [IWeakAnimal]
+ */
+function weak_animals(args: {
+ requests: $ReadOnlyArray<{ofType: string}>,
+}): Array {
+ return args.requests.map(request => {
+ return weak_animal({request});
+ });
+}
+
+module.exports = {
+ weak_animal,
+ weak_animals,
+};
diff --git a/packages/relay-runtime/store/__tests__/resolvers/__generated__/Query__weak_animal$normalization.graphql.js b/packages/relay-runtime/store/__tests__/resolvers/__generated__/Query__weak_animal$normalization.graphql.js
new file mode 100644
index 0000000000000..eb088dd198661
--- /dev/null
+++ b/packages/relay-runtime/store/__tests__/resolvers/__generated__/Query__weak_animal$normalization.graphql.js
@@ -0,0 +1,77 @@
+/**
+ * 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.
+ *
+ * @oncall relay
+ *
+ * @generated SignedSource<<95b83bdc6bd0b02dcaf343e68c8e7782>>
+ * @flow
+ * @lightSyntaxTransform
+ * @nogrep
+ */
+
+/* eslint-disable */
+
+'use strict';
+
+/*::
+import type { NormalizationSplitOperation } from 'relay-runtime';
+
+import type { Octopus } from "../OctopusResolvers.js";
+import type { PurpleOctopus } from "../PurpleOctopusResolvers.js";
+export type Query__weak_animal$normalization = {|
+ +__typename: "Octopus",
+ +__relay_model_instance: Octopus,
+|} | {|
+ +__typename: "PurpleOctopus",
+ +__relay_model_instance: PurpleOctopus,
+|};
+
+*/
+
+var node/*: NormalizationSplitOperation*/ = (function(){
+var v0 = [
+ {
+ "alias": null,
+ "args": null,
+ "kind": "ScalarField",
+ "name": "__relay_model_instance",
+ "storageKey": null
+ },
+ {
+ "alias": null,
+ "args": null,
+ "kind": "ScalarField",
+ "name": "__typename",
+ "storageKey": null
+ }
+];
+return {
+ "kind": "SplitOperation",
+ "metadata": {},
+ "name": "Query__weak_animal$normalization",
+ "selections": [
+ {
+ "kind": "ClientExtension",
+ "selections": [
+ {
+ "kind": "InlineFragment",
+ "selections": (v0/*: any*/),
+ "type": "Octopus",
+ "abstractKey": null
+ },
+ {
+ "kind": "InlineFragment",
+ "selections": (v0/*: any*/),
+ "type": "PurpleOctopus",
+ "abstractKey": null
+ }
+ ]
+ }
+ ]
+};
+})();
+
+module.exports = node;
diff --git a/packages/relay-runtime/store/__tests__/resolvers/__generated__/Query__weak_animals$normalization.graphql.js b/packages/relay-runtime/store/__tests__/resolvers/__generated__/Query__weak_animals$normalization.graphql.js
new file mode 100644
index 0000000000000..2f36308b1051c
--- /dev/null
+++ b/packages/relay-runtime/store/__tests__/resolvers/__generated__/Query__weak_animals$normalization.graphql.js
@@ -0,0 +1,77 @@
+/**
+ * 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.
+ *
+ * @oncall relay
+ *
+ * @generated SignedSource<<782803abb3250f42d9b6fe3b8055e3db>>
+ * @flow
+ * @lightSyntaxTransform
+ * @nogrep
+ */
+
+/* eslint-disable */
+
+'use strict';
+
+/*::
+import type { NormalizationSplitOperation } from 'relay-runtime';
+
+import type { Octopus } from "../OctopusResolvers.js";
+import type { PurpleOctopus } from "../PurpleOctopusResolvers.js";
+export type Query__weak_animals$normalization = {|
+ +__typename: "Octopus",
+ +__relay_model_instance: Octopus,
+|} | {|
+ +__typename: "PurpleOctopus",
+ +__relay_model_instance: PurpleOctopus,
+|};
+
+*/
+
+var node/*: NormalizationSplitOperation*/ = (function(){
+var v0 = [
+ {
+ "alias": null,
+ "args": null,
+ "kind": "ScalarField",
+ "name": "__relay_model_instance",
+ "storageKey": null
+ },
+ {
+ "alias": null,
+ "args": null,
+ "kind": "ScalarField",
+ "name": "__typename",
+ "storageKey": null
+ }
+];
+return {
+ "kind": "SplitOperation",
+ "metadata": {},
+ "name": "Query__weak_animals$normalization",
+ "selections": [
+ {
+ "kind": "ClientExtension",
+ "selections": [
+ {
+ "kind": "InlineFragment",
+ "selections": (v0/*: any*/),
+ "type": "Octopus",
+ "abstractKey": null
+ },
+ {
+ "kind": "InlineFragment",
+ "selections": (v0/*: any*/),
+ "type": "PurpleOctopus",
+ "abstractKey": null
+ }
+ ]
+ }
+ ]
+};
+})();
+
+module.exports = node;
diff --git a/packages/relay-test-utils-internal/schema-extensions/IAnimal.graphql b/packages/relay-test-utils-internal/schema-extensions/IAnimal.graphql
index d5e210356e96b..fb97639897c59 100644
--- a/packages/relay-test-utils-internal/schema-extensions/IAnimal.graphql
+++ b/packages/relay-test-utils-internal/schema-extensions/IAnimal.graphql
@@ -27,3 +27,10 @@ extend type Query {
interface IWeakAnimal {
legs: Int
}
+
+input WeakAnimalRequest {
+ """
+ Should be a valid GraphQL type that implements `IWeakAnimal`.
+ """
+ ofType: String!
+}