Skip to content

Commit

Permalink
unions that implement interfaces HAVE fields
Browse files Browse the repository at this point in the history
  • Loading branch information
yaacovCR committed May 23, 2022
1 parent 2f4d7d3 commit 6f229f7
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 14 deletions.
19 changes: 19 additions & 0 deletions src/jsutils/mapValues.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import type { ObjMap, ReadOnlyObjMap } from './ObjMap';

/**
* Creates an object map from an array of `maps` with the same keys as each `map`
* in `maps` and values generated by running each value of `map` thru `fn`.
*/
export function mapValues<T, V>(
maps: ReadonlyArray<ReadOnlyObjMap<T>>,
fn: (value: T, key: string) => V,
): ObjMap<V> {
const result = Object.create(null);

for (const map of maps) {
for (const key of Object.keys(map)) {
result[key] = fn(map[key], key);
}
}
return result;
}
15 changes: 15 additions & 0 deletions src/type/definition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { isObjectLike } from '../jsutils/isObjectLike';
import { keyMap } from '../jsutils/keyMap';
import { keyValMap } from '../jsutils/keyValMap';
import { mapValue } from '../jsutils/mapValue';
import { mapValues } from '../jsutils/mapValues';
import type { Maybe } from '../jsutils/Maybe';
import type { ObjMap } from '../jsutils/ObjMap';
import type { Path } from '../jsutils/Path';
Expand Down Expand Up @@ -1216,6 +1217,7 @@ export class GraphQLUnionType {
astNode: Maybe<UnionTypeDefinitionNode>;
extensionASTNodes: ReadonlyArray<UnionTypeExtensionNode>;

private _fields: GraphQLFieldMap<any, any> | undefined;
private _types: ThunkReadonlyArray<GraphQLObjectType>;
private _interfaces: ThunkReadonlyArray<GraphQLInterfaceType>;

Expand All @@ -1240,6 +1242,19 @@ export class GraphQLUnionType {
return 'GraphQLUnionType';
}

getFields(): GraphQLFieldMap<any, any> {
if (this._fields !== undefined) {
return this._fields;
}

this._fields = mapValues(
this.getInterfaces().map((iface) => iface.getFields()),
identityFunc,
);

return this._fields;
}

getTypes(): ReadonlyArray<GraphQLObjectType> {
if (typeof this._types === 'function') {
this._types = this._types();
Expand Down
12 changes: 1 addition & 11 deletions src/utilities/TypeInfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,9 @@ import {
isEnumType,
isInputObjectType,
isInputType,
isInterfaceType,
isListType,
isObjectType,
isOutputType,
isUnionType,
} from '../type/definition';
import type { GraphQLDirective } from '../type/directives';
import {
Expand Down Expand Up @@ -321,17 +319,9 @@ function getFieldDef(
if (name === TypeNameMetaFieldDef.name && isCompositeType(parentType)) {
return TypeNameMetaFieldDef;
}
if (isObjectType(parentType) || isInterfaceType(parentType)) {
if (isCompositeType(parentType)) {
return parentType.getFields()[name];
}
if (isUnionType(parentType)) {
for (const iface of parentType.getInterfaces()) {
const field = iface.getFields()[name];
if (field) {
return field;
}
}
}
}

/**
Expand Down
14 changes: 11 additions & 3 deletions src/validation/__tests__/FieldsOnCorrectTypeRule-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,21 @@ const testSchema = buildSchema(`
union CatOrDog = Cat | Dog
union ConstrainedCatOrDog implements Pet = Cat | Dog
interface Named {
name: String
}
type Human {
type Human implements Named {
name: String
pets: [Pet]
}
type Alien implements Named {
name: String
}
union NamedHumanOrAlien implements Named = Human | Alien
type Query {
human: Human
}
Expand Down Expand Up @@ -85,7 +93,7 @@ describe('Validate: Fields on correct type', () => {

it('Union implementing interface field selection', () => {
expectValid(`
fragment unionImplementingInterfaceFieldSelection on ConstrainedCatOrDog {
fragment unionImplementingInterfaceFieldSelection on HumanOrAlien {
__typename
name
}
Expand Down

0 comments on commit 6f229f7

Please sign in to comment.