Skip to content

Commit b995ed1

Browse files
authored
Use parent type name on interface types without fragments (#10503)
1 parent bec7e74 commit b995ed1

File tree

3 files changed

+116
-1
lines changed

3 files changed

+116
-1
lines changed

.changeset/odd-crabs-grab.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@graphql-codegen/visitor-plugin-common': patch
3+
---
4+
5+
Use parent type name on interface types without fragments

packages/plugins/other/visitor-plugin-common/src/selection-set-to-object.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -958,7 +958,24 @@ export class SelectionSetToObject<Config extends ParsedDocumentsConfig = ParsedD
958958
protected buildParentFieldName(typeName: string, parentName: string): string {
959959
// queries/mutations/fragments are guaranteed to be unique type names,
960960
// so we can skip affixing the field names with typeName
961-
return operationTypes.includes(typeName) ? parentName : `${parentName}_${typeName}`;
961+
if (operationTypes.includes(typeName)) {
962+
return parentName;
963+
}
964+
965+
// When the parent schema type is an interface, use the interface name instead of the concrete type
966+
// BUT only if we're not inside a fragment (fragments explicitly target specific types)
967+
const schemaType = this._schema.getType(typeName);
968+
const isInFragment = parentName.includes('Fragment');
969+
if (
970+
isObjectType(schemaType) &&
971+
this._parentSchemaType &&
972+
isInterfaceType(this._parentSchemaType) &&
973+
!isInFragment
974+
) {
975+
return `${parentName}_${this._parentSchemaType.name}`;
976+
}
977+
978+
return `${parentName}_${typeName}`;
962979
}
963980
}
964981

packages/plugins/typescript/operations/tests/extract-all-types.spec.ts

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1462,4 +1462,97 @@ describe('extractAllFieldsToTypes: true', () => {
14621462

14631463
await validate(content, config, complexTestSchemaWithUnionsAndInterfaces);
14641464
});
1465+
it('should handle interfaces without fragments', async () => {
1466+
const nestedInterfacesSchema = buildSchema(/* GraphQL */ `
1467+
type Query {
1468+
animals: [Animal!]
1469+
}
1470+
1471+
interface Animal {
1472+
name: String!
1473+
owner: Person!
1474+
}
1475+
1476+
type Cat implements Animal {
1477+
name: String!
1478+
owner: Person!
1479+
}
1480+
1481+
type Dog implements Animal {
1482+
name: String!
1483+
owner: Person!
1484+
}
1485+
1486+
interface Person {
1487+
name: String!
1488+
}
1489+
1490+
type Trainer implements Person {
1491+
name: String!
1492+
}
1493+
1494+
type Veterinarian implements Person {
1495+
name: String!
1496+
}
1497+
`);
1498+
1499+
const nestedInterfacesQuery = parse(/* GraphQL */ `
1500+
query GetAnimals {
1501+
animals {
1502+
name
1503+
owner {
1504+
name
1505+
}
1506+
}
1507+
}
1508+
`);
1509+
1510+
const config: TypeScriptDocumentsPluginConfig = {
1511+
preResolveTypes: true,
1512+
extractAllFieldsToTypes: true,
1513+
nonOptionalTypename: true,
1514+
dedupeOperationSuffix: true,
1515+
};
1516+
1517+
const { content } = await plugin(
1518+
nestedInterfacesSchema,
1519+
[{ location: 'test-file.ts', document: nestedInterfacesQuery }],
1520+
config,
1521+
{ outputFile: '' }
1522+
);
1523+
1524+
// Issue #10502: When nested interfaces have the same fields, extractAllFieldsToTypes
1525+
// We need to use the interface name for the nested type name.
1526+
1527+
expect(content).toMatchInlineSnapshot(`
1528+
"export type GetAnimalsQuery_animals_Animal_owner_Trainer = { __typename: 'Trainer', name: string };
1529+
1530+
export type GetAnimalsQuery_animals_Animal_owner_Veterinarian = { __typename: 'Veterinarian', name: string };
1531+
1532+
export type GetAnimalsQuery_animals_Animal_owner =
1533+
| GetAnimalsQuery_animals_Animal_owner_Trainer
1534+
| GetAnimalsQuery_animals_Animal_owner_Veterinarian
1535+
;
1536+
1537+
export type GetAnimalsQuery_animals_Cat = { __typename: 'Cat', name: string, owner: GetAnimalsQuery_animals_Animal_owner };
1538+
1539+
export type GetAnimalsQuery_animals_Dog = { __typename: 'Dog', name: string, owner: GetAnimalsQuery_animals_Animal_owner };
1540+
1541+
export type GetAnimalsQuery_animals =
1542+
| GetAnimalsQuery_animals_Cat
1543+
| GetAnimalsQuery_animals_Dog
1544+
;
1545+
1546+
export type GetAnimalsQuery_Query = { __typename: 'Query', animals?: Array<GetAnimalsQuery_animals> | null };
1547+
1548+
1549+
export type GetAnimalsQueryVariables = Exact<{ [key: string]: never; }>;
1550+
1551+
1552+
export type GetAnimalsQuery = GetAnimalsQuery_Query;
1553+
"
1554+
`);
1555+
1556+
await validate(content, config, nestedInterfacesSchema);
1557+
});
14651558
});

0 commit comments

Comments
 (0)