Skip to content

Commit aff18e6

Browse files
committed
Extract type comparators into the public API
This adds the utilities isEqualType, isTypeSubTypeOf, and doTypesOverlap to the public API.
1 parent 5ee1edc commit aff18e6

File tree

4 files changed

+63
-26
lines changed

4 files changed

+63
-26
lines changed

src/index.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,4 +154,9 @@ export {
154154

155155
// Concatenates multiple AST together.
156156
concatAST,
157+
158+
// Comparators for types
159+
isEqualType,
160+
isTypeSubTypeOf,
161+
doTypesOverlap,
157162
} from './utilities';

src/utilities/index.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,10 @@ export { isValidLiteralValue } from './isValidLiteralValue';
4747

4848
// Concatenates multiple AST together.
4949
export { concatAST } from './concatAST';
50+
51+
// Comparators for types
52+
export {
53+
isEqualType,
54+
isTypeSubTypeOf,
55+
doTypesOverlap
56+
} from './typeComparators';

src/utilities/typeComparators.js

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,16 @@
1111
import {
1212
isAbstractType,
1313
GraphQLObjectType,
14+
GraphQLInterfaceType,
15+
GraphQLUnionType,
1416
GraphQLList,
1517
GraphQLNonNull,
1618
} from '../type/definition';
17-
import type { GraphQLType, GraphQLAbstractType } from '../type/definition';
19+
import type {
20+
GraphQLType,
21+
GraphQLCompositeType,
22+
GraphQLAbstractType
23+
} from '../type/definition';
1824

1925

2026
/**
@@ -86,3 +92,46 @@ export function isTypeSubTypeOf(
8692
// Otherwise, the child type is not a valid subtype of the parent type.
8793
return false;
8894
}
95+
96+
/**
97+
* Provided two composite types, determine if they "overlap". Two composite
98+
* types overlap when the Sets of possible concrete types for each intersect.
99+
*
100+
* This is often used to determine if a fragment of a given type could possibly
101+
* be visited in a context of another type.
102+
*
103+
* This function is commutative.
104+
*/
105+
export function doTypesOverlap(
106+
typeA: GraphQLCompositeType,
107+
typeB: GraphQLCompositeType
108+
): boolean {
109+
// So flow is aware this is constant
110+
const _typeB = typeB;
111+
112+
// Equivalent types overlap
113+
if (typeA === _typeB) {
114+
return true;
115+
}
116+
117+
if (typeA instanceof GraphQLInterfaceType ||
118+
typeA instanceof GraphQLUnionType) {
119+
if (_typeB instanceof GraphQLInterfaceType ||
120+
_typeB instanceof GraphQLUnionType) {
121+
// If both types are abstract, then determine if there is any intersection
122+
// between possible concrete types of each.
123+
return typeA.getPossibleTypes().some(type => _typeB.isPossibleType(type));
124+
}
125+
// Determine if the latter type is a possible concrete type of the former.
126+
return typeA.isPossibleType(_typeB);
127+
}
128+
129+
if (_typeB instanceof GraphQLInterfaceType ||
130+
_typeB instanceof GraphQLUnionType) {
131+
// Determine if the former type is a possible concrete type of the latter.
132+
return _typeB.isPossibleType(typeA);
133+
}
134+
135+
// Otherwise the types do not overlap.
136+
return false;
137+
}

src/validation/rules/PossibleFragmentSpreads.js

Lines changed: 1 addition & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,7 @@
1010

1111
import type { ValidationContext } from '../index';
1212
import { GraphQLError } from '../../error';
13-
import keyMap from '../../jsutils/keyMap';
14-
import {
15-
GraphQLObjectType,
16-
GraphQLInterfaceType,
17-
GraphQLUnionType
18-
} from '../../type/definition';
13+
import { doTypesOverlap } from '../../utilities/typeComparators';
1914
import { typeFromAST } from '../../utilities/typeFromAST';
2015
import type { GraphQLType } from '../../type/definition';
2116

@@ -74,22 +69,3 @@ function getFragmentType(context, name) {
7469
const frag = context.getFragment(name);
7570
return frag && typeFromAST(context.getSchema(), frag.typeCondition);
7671
}
77-
78-
function doTypesOverlap(t1, t2) {
79-
if (t1 === t2) {
80-
return true;
81-
}
82-
if (t1 instanceof GraphQLObjectType) {
83-
if (t2 instanceof GraphQLObjectType) {
84-
return false;
85-
}
86-
return t2.getPossibleTypes().indexOf(t1) !== -1;
87-
}
88-
if (t1 instanceof GraphQLInterfaceType || t1 instanceof GraphQLUnionType) {
89-
if (t2 instanceof GraphQLObjectType) {
90-
return t1.getPossibleTypes().indexOf(t2) !== -1;
91-
}
92-
const t1TypeNames = keyMap(t1.getPossibleTypes(), type => type.name);
93-
return t2.getPossibleTypes().some(type => t1TypeNames[type.name]);
94-
}
95-
}

0 commit comments

Comments
 (0)