Skip to content

Commit 487cbd1

Browse files
committed
allow unions to include abstract types
1 parent ffe37cb commit 487cbd1

18 files changed

+417
-56
lines changed

benchmark/github-schema.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -56058,7 +56058,7 @@
5605856058
},
5605956059
{
5606056060
"name": "UNION",
56061-
"description": "Indicates this type is a union. `possibleTypes` is a valid field.",
56061+
"description": "Indicates this type is a union. `memberTypes` and `possibleTypes` are valid fields.",
5606256062
"isDeprecated": false,
5606356063
"deprecationReason": null
5606456064
},

docs-old/APIReference-TypeSystem.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -378,12 +378,12 @@ class GraphQLUnionType {
378378

379379
type GraphQLUnionTypeConfig = {
380380
name: string,
381-
types: GraphQLObjectsThunk | Array<GraphQLObjectType>,
381+
types: GraphQLCompositesThunk | Array<GraphQLCompositeType>,
382382
resolveType?: (value: any, info?: GraphQLResolveInfo) => ?GraphQLObjectType;
383383
description?: ?string;
384384
};
385385

386-
type GraphQLObjectsThunk = () => Array<GraphQLObjectType>;
386+
type GraphQLCompositesThunk = () => Array<GraphQLCompositeType>;
387387
```
388388
389389
When a field can return one of a heterogeneous set of types, a Union type

src/type/__tests__/definition-test.ts

+7-6
Original file line numberDiff line numberDiff line change
@@ -375,23 +375,24 @@ describe('Type System: Unions', () => {
375375
name: 'SomeUnion',
376376
types: [ObjectType],
377377
});
378-
expect(unionType.getTypes()).to.deep.equal([ObjectType]);
378+
expect(unionType.getMemberTypes()).to.deep.equal([ObjectType]);
379+
expect(unionType.getPossibleTypes()).to.deep.equal([ObjectType]);
379380
});
380381

381382
it('accepts a Union type with function returning an array of types', () => {
382383
const unionType = new GraphQLUnionType({
383384
name: 'SomeUnion',
384385
types: () => [ObjectType],
385386
});
386-
expect(unionType.getTypes()).to.deep.equal([ObjectType]);
387+
expect(unionType.getMemberTypes()).to.deep.equal([ObjectType]);
387388
});
388389

389-
it('accepts a Union type without types', () => {
390-
const unionType = new GraphQLUnionType({
390+
it('accepts a recursive Union type', () => {
391+
const unionType: GraphQLUnionType = new GraphQLUnionType({
391392
name: 'SomeUnion',
392-
types: [],
393+
types: () => [unionType],
393394
});
394-
expect(unionType.getTypes()).to.deep.equal([]);
395+
expect(unionType.getMemberTypes()).to.deep.equal([unionType]);
395396
});
396397

397398
it('rejects an Union type with invalid name', () => {

src/type/__tests__/introspection-test.ts

+94
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ describe('Introspection', () => {
2626
descriptions: false,
2727
specifiedByUrl: true,
2828
directiveIsRepeatable: true,
29+
memberTypes: true,
2930
});
3031

3132
const result = graphqlSync({ schema, source });
@@ -56,6 +57,7 @@ describe('Introspection', () => {
5657
inputFields: null,
5758
interfaces: [],
5859
enumValues: null,
60+
memberTypes: null,
5961
possibleTypes: null,
6062
},
6163
{
@@ -66,6 +68,7 @@ describe('Introspection', () => {
6668
inputFields: null,
6769
interfaces: null,
6870
enumValues: null,
71+
memberTypes: null,
6972
possibleTypes: null,
7073
},
7174
{
@@ -76,6 +79,7 @@ describe('Introspection', () => {
7679
inputFields: null,
7780
interfaces: null,
7881
enumValues: null,
82+
memberTypes: null,
7983
possibleTypes: null,
8084
},
8185
{
@@ -181,6 +185,7 @@ describe('Introspection', () => {
181185
inputFields: null,
182186
interfaces: [],
183187
enumValues: null,
188+
memberTypes: null,
184189
possibleTypes: null,
185190
},
186191
{
@@ -284,6 +289,25 @@ describe('Introspection', () => {
284289
isDeprecated: false,
285290
deprecationReason: null,
286291
},
292+
{
293+
name: 'memberTypes',
294+
args: [],
295+
type: {
296+
kind: 'LIST',
297+
name: null,
298+
ofType: {
299+
kind: 'NON_NULL',
300+
name: null,
301+
ofType: {
302+
kind: 'OBJECT',
303+
name: '__Type',
304+
ofType: null,
305+
},
306+
},
307+
},
308+
isDeprecated: false,
309+
deprecationReason: null,
310+
},
287311
{
288312
name: 'possibleTypes',
289313
args: [],
@@ -376,6 +400,7 @@ describe('Introspection', () => {
376400
inputFields: null,
377401
interfaces: [],
378402
enumValues: null,
403+
memberTypes: null,
379404
possibleTypes: null,
380405
},
381406
{
@@ -427,6 +452,7 @@ describe('Introspection', () => {
427452
deprecationReason: null,
428453
},
429454
],
455+
memberTypes: null,
430456
possibleTypes: null,
431457
},
432458
{
@@ -538,6 +564,7 @@ describe('Introspection', () => {
538564
inputFields: null,
539565
interfaces: [],
540566
enumValues: null,
567+
memberTypes: null,
541568
possibleTypes: null,
542569
},
543570
{
@@ -627,6 +654,7 @@ describe('Introspection', () => {
627654
inputFields: null,
628655
interfaces: [],
629656
enumValues: null,
657+
memberTypes: null,
630658
possibleTypes: null,
631659
},
632660
{
@@ -690,6 +718,7 @@ describe('Introspection', () => {
690718
inputFields: null,
691719
interfaces: [],
692720
enumValues: null,
721+
memberTypes: null,
693722
possibleTypes: null,
694723
},
695724
{
@@ -798,6 +827,7 @@ describe('Introspection', () => {
798827
inputFields: null,
799828
interfaces: [],
800829
enumValues: null,
830+
memberTypes: null,
801831
possibleTypes: null,
802832
},
803833
{
@@ -904,6 +934,7 @@ describe('Introspection', () => {
904934
deprecationReason: null,
905935
},
906936
],
937+
memberTypes: null,
907938
possibleTypes: null,
908939
},
909940
],
@@ -1612,6 +1643,68 @@ describe('Introspection', () => {
16121643
});
16131644
});
16141645

1646+
it('exposes memberTypes for Union types', () => {
1647+
const schema = buildSchema(`
1648+
union SomeUnion = SomeObject
1649+
1650+
union AnotherUnion = SomeUnion | SomeObject
1651+
1652+
type SomeObject {
1653+
someField(arg: String): String
1654+
}
1655+
1656+
schema {
1657+
query: SomeObject
1658+
}
1659+
`);
1660+
1661+
const source = `
1662+
{
1663+
SomeObject: __type(name: "SomeObject") {
1664+
memberTypes {
1665+
name
1666+
}
1667+
possibleTypes {
1668+
name
1669+
}
1670+
}
1671+
SomeUnion: __type(name: "SomeUnion") {
1672+
memberTypes {
1673+
name
1674+
}
1675+
possibleTypes {
1676+
name
1677+
}
1678+
}
1679+
AnotherUnion: __type(name: "AnotherUnion") {
1680+
memberTypes {
1681+
name
1682+
}
1683+
possibleTypes {
1684+
name
1685+
}
1686+
}
1687+
}
1688+
`;
1689+
1690+
expect(graphqlSync({ schema, source })).to.deep.equal({
1691+
data: {
1692+
SomeObject: {
1693+
memberTypes: null,
1694+
possibleTypes: null,
1695+
},
1696+
SomeUnion: {
1697+
memberTypes: [{ name: 'SomeObject' }],
1698+
possibleTypes: [{ name: 'SomeObject' }],
1699+
},
1700+
AnotherUnion: {
1701+
memberTypes: [{ name: 'SomeUnion' }, { name: 'SomeObject' }],
1702+
possibleTypes: [{ name: 'SomeObject' }],
1703+
},
1704+
},
1705+
});
1706+
});
1707+
16151708
it('executes an introspection query without calling global resolvers', () => {
16161709
const schema = buildSchema(`
16171710
type Query {
@@ -1623,6 +1716,7 @@ describe('Introspection', () => {
16231716
specifiedByUrl: true,
16241717
directiveIsRepeatable: true,
16251718
schemaDescription: true,
1719+
memberTypes: true,
16261720
});
16271721

16281722
/* c8 ignore start */

0 commit comments

Comments
 (0)