diff --git a/compiler/crates/relay-typegen/src/visit.rs b/compiler/crates/relay-typegen/src/visit.rs index d6666ca63b45..1f59d1ea1df0 100644 --- a/compiler/crates/relay-typegen/src/visit.rs +++ b/compiler/crates/relay-typegen/src/visit.rs @@ -2389,6 +2389,24 @@ pub(crate) fn raw_response_visit_selections( ), } } + + if let Some(concrete_type) = enclosing_linked_field_concrete_type { + // If we are generating for a concrete field type, we should 1. remove + // any selections that are for a different concrete type, since they are + // not applicable to our field, and 2. mark any selections without a + // concrete type as being for our field's concrete type, so that + // `raw_response_selections_to_babel` doesn't generate redundant types. + type_selections.retain_mut(|type_selection| { + match type_selection.get_enclosing_concrete_type() { + Some(selection_concrete_type) => selection_concrete_type == concrete_type, + None => { + type_selection.set_concrete_type(concrete_type); + true + } + } + }); + } + type_selections } diff --git a/compiler/crates/relay-typegen/tests/generate_flow/fixtures/relay-resolver-raw-response.expected b/compiler/crates/relay-typegen/tests/generate_flow/fixtures/relay-resolver-raw-response.expected index 00cd81ef72ab..3c61a9bb11e5 100644 --- a/compiler/crates/relay-typegen/tests/generate_flow/fixtures/relay-resolver-raw-response.expected +++ b/compiler/crates/relay-typegen/tests/generate_flow/fixtures/relay-resolver-raw-response.expected @@ -31,12 +31,10 @@ export type relayResolver_Query$data = {| |}, |}; export type relayResolver_Query$rawResponse = {| - +me: ?({| - +id: string, - |} | {| + +me: ?{| +id: string, +name: ?string, - |}), + |}, |}; export type relayResolver_Query = {| rawResponse: relayResolver_Query$rawResponse, diff --git a/compiler/crates/relay-typegen/tests/generate_flow/fixtures/spread-interface-fragment-on-concrete-raw-type.expected b/compiler/crates/relay-typegen/tests/generate_flow/fixtures/spread-interface-fragment-on-concrete-raw-type.expected new file mode 100644 index 000000000000..e1dac0e97193 --- /dev/null +++ b/compiler/crates/relay-typegen/tests/generate_flow/fixtures/spread-interface-fragment-on-concrete-raw-type.expected @@ -0,0 +1,84 @@ +==================================== INPUT ==================================== +fragment MyFragment on Actor { + name + ... on User { + canViewerLike + } + ... on Page { + subscribeStatus + } +} + +query MyQuery @raw_response_type { + me { + canViewerComment + ...MyFragment + } +} + +mutation MyMutation @raw_response_type { + setName(name: "test") { + canViewerComment + ...MyFragment + } +} +==================================== OUTPUT =================================== +import type { MyFragment$fragmentType } from "MyFragment.graphql"; +export type MyMutation$variables = {||}; +export type MyMutation$data = {| + +setName: ?{| + +canViewerComment: ?CustomBoolean, + +$fragmentSpreads: MyFragment$fragmentType, + |}, +|}; +export type MyMutation$rawResponse = {| + +setName: ?{| + +__isActor: "User", + +canViewerComment: ?CustomBoolean, + +canViewerLike: ?CustomBoolean, + +id: string, + +name: ?string, + |}, +|}; +export type MyMutation = {| + rawResponse: MyMutation$rawResponse, + response: MyMutation$data, + variables: MyMutation$variables, +|}; +------------------------------------------------------------------------------- +import type { MyFragment$fragmentType } from "MyFragment.graphql"; +export type MyQuery$variables = {||}; +export type MyQuery$data = {| + +me: ?{| + +canViewerComment: ?CustomBoolean, + +$fragmentSpreads: MyFragment$fragmentType, + |}, +|}; +export type MyQuery$rawResponse = {| + +me: ?{| + +__isActor: "User", + +canViewerComment: ?CustomBoolean, + +canViewerLike: ?CustomBoolean, + +id: string, + +name: ?string, + |}, +|}; +export type MyQuery = {| + rawResponse: MyQuery$rawResponse, + response: MyQuery$data, + variables: MyQuery$variables, +|}; +------------------------------------------------------------------------------- +import type { FragmentType } from "relay-runtime"; +declare export opaque type MyFragment$fragmentType: FragmentType; +export type MyFragment$data = {| + +canViewerLike?: ?CustomBoolean, + +name: ?string, + +subscribeStatus?: ?string, + +$fragmentType: MyFragment$fragmentType, +|}; +export type MyFragment$key = { + +$data?: MyFragment$data, + +$fragmentSpreads: MyFragment$fragmentType, + ... +}; diff --git a/compiler/crates/relay-typegen/tests/generate_flow/fixtures/spread-interface-fragment-on-concrete-raw-type.graphql b/compiler/crates/relay-typegen/tests/generate_flow/fixtures/spread-interface-fragment-on-concrete-raw-type.graphql new file mode 100644 index 000000000000..2a00b64f1401 --- /dev/null +++ b/compiler/crates/relay-typegen/tests/generate_flow/fixtures/spread-interface-fragment-on-concrete-raw-type.graphql @@ -0,0 +1,23 @@ +fragment MyFragment on Actor { + name + ... on User { + canViewerLike + } + ... on Page { + subscribeStatus + } +} + +query MyQuery @raw_response_type { + me { + canViewerComment + ...MyFragment + } +} + +mutation MyMutation @raw_response_type { + setName(name: "test") { + canViewerComment + ...MyFragment + } +} diff --git a/compiler/crates/relay-typegen/tests/generate_flow_test.rs b/compiler/crates/relay-typegen/tests/generate_flow_test.rs index b4413c90c643..b2870d356a7b 100644 --- a/compiler/crates/relay-typegen/tests/generate_flow_test.rs +++ b/compiler/crates/relay-typegen/tests/generate_flow_test.rs @@ -992,6 +992,13 @@ async fn simple() { test_fixture(transform_fixture, file!(), "simple.graphql", "generate_flow/fixtures/simple.expected", input, expected).await; } +#[tokio::test] +async fn spread_interface_fragment_on_concrete_raw_type() { + let input = include_str!("generate_flow/fixtures/spread-interface-fragment-on-concrete-raw-type.graphql"); + let expected = include_str!("generate_flow/fixtures/spread-interface-fragment-on-concrete-raw-type.expected"); + test_fixture(transform_fixture, file!(), "spread-interface-fragment-on-concrete-raw-type.graphql", "generate_flow/fixtures/spread-interface-fragment-on-concrete-raw-type.expected", input, expected).await; +} + #[tokio::test] async fn typename_in_union_with_other_fields() { let input = include_str!("generate_flow/fixtures/typename-in-union-with-other-fields.graphql"); diff --git a/compiler/crates/relay-typegen/tests/generate_typescript/fixtures/relay-resolver-on-query-with-output-type.expected b/compiler/crates/relay-typegen/tests/generate_typescript/fixtures/relay-resolver-on-query-with-output-type.expected index ef682bea0927..15dadf4af6ca 100644 --- a/compiler/crates/relay-typegen/tests/generate_typescript/fixtures/relay-resolver-on-query-with-output-type.expected +++ b/compiler/crates/relay-typegen/tests/generate_typescript/fixtures/relay-resolver-on-query-with-output-type.expected @@ -46,8 +46,6 @@ export type Foo_user$rawResponse = { readonly id: string; readonly lastName: string | null | undefined; }>; - } | { - readonly id: string; } | null | undefined; }; export type Foo_user = { diff --git a/compiler/crates/relay-typegen/tests/generate_typescript/fixtures/spread-interface-fragment-on-concrete-raw-type.expected b/compiler/crates/relay-typegen/tests/generate_typescript/fixtures/spread-interface-fragment-on-concrete-raw-type.expected new file mode 100644 index 000000000000..4849c24da80d --- /dev/null +++ b/compiler/crates/relay-typegen/tests/generate_typescript/fixtures/spread-interface-fragment-on-concrete-raw-type.expected @@ -0,0 +1,82 @@ +==================================== INPUT ==================================== +fragment MyFragment on Actor { + name + ... on User { + canViewerLike + } + ... on Page { + subscribeStatus + } +} + +query MyQuery @raw_response_type { + me { + canViewerComment + ...MyFragment + } +} + +mutation MyMutation @raw_response_type { + setName(name: "test") { + canViewerComment + ...MyFragment + } +} +==================================== OUTPUT =================================== +import { FragmentRefs } from "relay-runtime"; +export type MyMutation$variables = Record; +export type MyMutation$data = { + readonly setName: { + readonly canViewerComment: boolean | null | undefined; + readonly " $fragmentSpreads": FragmentRefs<"MyFragment">; + } | null | undefined; +}; +export type MyMutation$rawResponse = { + readonly setName: { + readonly __isActor: "User"; + readonly canViewerComment: boolean | null | undefined; + readonly canViewerLike: boolean | null | undefined; + readonly id: string; + readonly name: string | null | undefined; + } | null | undefined; +}; +export type MyMutation = { + rawResponse: MyMutation$rawResponse; + response: MyMutation$data; + variables: MyMutation$variables; +}; +------------------------------------------------------------------------------- +import { FragmentRefs } from "relay-runtime"; +export type MyQuery$variables = Record; +export type MyQuery$data = { + readonly me: { + readonly canViewerComment: boolean | null | undefined; + readonly " $fragmentSpreads": FragmentRefs<"MyFragment">; + } | null | undefined; +}; +export type MyQuery$rawResponse = { + readonly me: { + readonly __isActor: "User"; + readonly canViewerComment: boolean | null | undefined; + readonly canViewerLike: boolean | null | undefined; + readonly id: string; + readonly name: string | null | undefined; + } | null | undefined; +}; +export type MyQuery = { + rawResponse: MyQuery$rawResponse; + response: MyQuery$data; + variables: MyQuery$variables; +}; +------------------------------------------------------------------------------- +import { FragmentRefs } from "relay-runtime"; +export type MyFragment$data = { + readonly canViewerLike?: boolean | null | undefined; + readonly name: string | null | undefined; + readonly subscribeStatus?: string | null | undefined; + readonly " $fragmentType": "MyFragment"; +}; +export type MyFragment$key = { + readonly " $data"?: MyFragment$data; + readonly " $fragmentSpreads": FragmentRefs<"MyFragment">; +}; diff --git a/compiler/crates/relay-typegen/tests/generate_typescript/fixtures/spread-interface-fragment-on-concrete-raw-type.graphql b/compiler/crates/relay-typegen/tests/generate_typescript/fixtures/spread-interface-fragment-on-concrete-raw-type.graphql new file mode 100644 index 000000000000..2a00b64f1401 --- /dev/null +++ b/compiler/crates/relay-typegen/tests/generate_typescript/fixtures/spread-interface-fragment-on-concrete-raw-type.graphql @@ -0,0 +1,23 @@ +fragment MyFragment on Actor { + name + ... on User { + canViewerLike + } + ... on Page { + subscribeStatus + } +} + +query MyQuery @raw_response_type { + me { + canViewerComment + ...MyFragment + } +} + +mutation MyMutation @raw_response_type { + setName(name: "test") { + canViewerComment + ...MyFragment + } +} diff --git a/compiler/crates/relay-typegen/tests/generate_typescript_test.rs b/compiler/crates/relay-typegen/tests/generate_typescript_test.rs index 081aa80df50c..4ea6f0dd96ac 100644 --- a/compiler/crates/relay-typegen/tests/generate_typescript_test.rs +++ b/compiler/crates/relay-typegen/tests/generate_typescript_test.rs @@ -593,6 +593,13 @@ async fn simple_use_import_type_syntax() { test_fixture(transform_fixture, file!(), "simple-use-import-type-syntax.graphql", "generate_typescript/fixtures/simple-use-import-type-syntax.expected", input, expected).await; } +#[tokio::test] +async fn spread_interface_fragment_on_concrete_raw_type() { + let input = include_str!("generate_typescript/fixtures/spread-interface-fragment-on-concrete-raw-type.graphql"); + let expected = include_str!("generate_typescript/fixtures/spread-interface-fragment-on-concrete-raw-type.expected"); + test_fixture(transform_fixture, file!(), "spread-interface-fragment-on-concrete-raw-type.graphql", "generate_typescript/fixtures/spread-interface-fragment-on-concrete-raw-type.expected", input, expected).await; +} + #[tokio::test] async fn typename_in_union_with_other_fields() { let input = include_str!("generate_typescript/fixtures/typename-in-union-with-other-fields.graphql");