Skip to content

Commit

Permalink
fix(unions): add function syntax for union types
Browse files Browse the repository at this point in the history
closes #364
  • Loading branch information
MichalLytek committed Aug 17, 2019
1 parent b86f660 commit 63f480b
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 6 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
- rename `DepreciationOptions` interface to `DeprecationOptions` and deprecate the old one
- update deps to newest minor versions (`tslib`, `semver`, `graphql-query-complexity` and `glob`)
- support nested array types (`@Field(type => [[Int]])`) (#393)
- deprecate the direct array syntax for union types

## Fixes
- fix errors on circular refs in union types (#364) by adding the function syntax (`() => TClassTypes`)

## v0.17.4
### Features
Expand Down
13 changes: 10 additions & 3 deletions src/decorators/unions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ export interface UnionTypeConfig<TClassTypes extends ClassType[]>
extends ResolveTypeOptions<UnionFromClasses<TClassTypes>> {
name: string;
description?: string;
types: TClassTypes;
/**
* The direct array syntax is deprecated.
* Use the function syntax `() => TClassTypes` instead.
*/
types: TClassTypes | (() => TClassTypes);
}

export function createUnionType<T extends ClassType[]>(
Expand All @@ -16,13 +20,16 @@ export function createUnionType<T extends ClassType[]>(
export function createUnionType({
name,
description,
types: classTypes,
types: classTypesOrClassTypesFn,
resolveType,
}: UnionTypeConfig<ClassType[]>): any {
const unionMetadataSymbol = getMetadataStorage().collectUnionMetadata({
name,
description,
classTypes,
getClassTypes:
typeof classTypesOrClassTypesFn === "function"
? classTypesOrClassTypesFn
: () => classTypesOrClassTypesFn,
resolveType,
});
return unionMetadataSymbol;
Expand Down
2 changes: 1 addition & 1 deletion src/metadata/definitions/union-metadata.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ClassType, TypeResolver } from "../../interfaces";

export interface UnionMetadata {
classTypes: ClassType[];
getClassTypes: () => ClassType[];
name: string;
description?: string;
resolveType?: TypeResolver<any, any>;
Expand Down
5 changes: 3 additions & 2 deletions src/schema/schema-generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,19 +145,20 @@ export abstract class SchemaGenerator {

private static buildTypesInfo() {
this.unionTypesInfo = getMetadataStorage().unions.map<UnionTypeInfo>(unionMetadata => {
const unionClassTypes = unionMetadata.getClassTypes();
return {
unionSymbol: unionMetadata.symbol,
type: new GraphQLUnionType({
name: unionMetadata.name,
description: unionMetadata.description,
types: () =>
unionMetadata.classTypes.map(
unionClassTypes.map(
objectType => this.objectTypesInfo.find(type => type.target === objectType)!.type,
),
resolveType: unionMetadata.resolveType
? this.getResolveTypeFunction(unionMetadata.resolveType)
: instance => {
const instanceTarget = unionMetadata.classTypes.find(
const instanceTarget = unionClassTypes.find(
ClassType => instance instanceof ClassType,
);
if (!instanceTarget) {
Expand Down
34 changes: 34 additions & 0 deletions tests/functional/unions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ describe("Unions", () => {
description: "OneTwoThreeUnion description",
types: [ObjectOne, ObjectTwo, ObjectThree],
});
const OneTwoThreeUnionFn = createUnionType({
name: "OneTwoThreeUnionFn",
description: "OneTwoThreeUnionFn description",
types: () => [ObjectOne, ObjectTwo, ObjectThree],
});

const UnionWithStringResolveType = createUnionType({
name: "UnionWithStringResolveType",
Expand Down Expand Up @@ -85,6 +90,13 @@ describe("Unions", () => {
return oneInstance;
}

@Query(returns => OneTwoThreeUnionFn)
getObjectOneFromUnionFn(): typeof OneTwoThreeUnionFn {
const oneInstance = new ObjectTwo();
oneInstance.fieldTwo = "fieldTwo";
return oneInstance;
}

@Query()
getObjectWithUnion(): ObjectUnion {
const oneInstance = new ObjectTwo();
Expand Down Expand Up @@ -147,6 +159,28 @@ describe("Unions", () => {
expect(objectThree.kind).toEqual(TypeKind.OBJECT);
});

it("should correctly generate union type from function syntax", async () => {
const oneTwoThreeUnionFnType = schemaIntrospection.types.find(
type => type.name === "OneTwoThreeUnionFn",
) as IntrospectionUnionType;
const objectOne = oneTwoThreeUnionFnType.possibleTypes.find(
type => type.name === "ObjectOne",
)!;
const objectTwo = oneTwoThreeUnionFnType.possibleTypes.find(
type => type.name === "ObjectTwo",
)!;
const objectThree = oneTwoThreeUnionFnType.possibleTypes.find(
type => type.name === "ObjectThree",
)!;

expect(oneTwoThreeUnionFnType.kind).toEqual(TypeKind.UNION);
expect(oneTwoThreeUnionFnType.name).toEqual("OneTwoThreeUnionFn");
expect(oneTwoThreeUnionFnType.description).toEqual("OneTwoThreeUnionFn description");
expect(objectOne.kind).toEqual(TypeKind.OBJECT);
expect(objectTwo.kind).toEqual(TypeKind.OBJECT);
expect(objectThree.kind).toEqual(TypeKind.OBJECT);
});

it("should correctly generate query's union output type", async () => {
const getObjectOneFromUnion = queryType.fields.find(
field => field.name === "getObjectOneFromUnion",
Expand Down

0 comments on commit 63f480b

Please sign in to comment.