diff --git a/CHANGELOG.md b/CHANGELOG.md index dfce8e10d..7bf3c4bbb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - **Breaking Change**: change the default `PrintSchemaOptions` option `commentDescriptions` to false (no more `#` comments in SDL) - add support for passing `PrintSchemaOptions` in `buildSchema.emitSchemaFile` (e.g. `commentDescriptions: true` to restore previous behavior) - add `buildTypeDefsAndResolvers` utils function for generating apollo-like `typeDefs` and `resolvers` pair (#233) +- add support for generic types (#255) ## Fixes - fix calling return type getter function `@Field(type => Foo)` before finishing module evaluation (allow for extending circular classes using `require`) diff --git a/src/decorators/InputType.ts b/src/decorators/InputType.ts index cf932c9bc..5b54b28a6 100644 --- a/src/decorators/InputType.ts +++ b/src/decorators/InputType.ts @@ -1,13 +1,15 @@ import { getMetadataStorage } from "../metadata/getMetadataStorage"; import { getNameDecoratorParams } from "../helpers/decorators"; -import { DescriptionOptions } from "./types"; +import { DescriptionOptions, AbstractClassOptions } from "./types"; + +export type InputTypeOptions = DescriptionOptions & AbstractClassOptions; export function InputType(): ClassDecorator; -export function InputType(options: DescriptionOptions): ClassDecorator; -export function InputType(name: string, options?: DescriptionOptions): ClassDecorator; +export function InputType(options: InputTypeOptions): ClassDecorator; +export function InputType(name: string, options?: InputTypeOptions): ClassDecorator; export function InputType( - nameOrOptions?: string | DescriptionOptions, - maybeOptions?: DescriptionOptions, + nameOrOptions?: string | InputTypeOptions, + maybeOptions?: InputTypeOptions, ): ClassDecorator { const { name, options } = getNameDecoratorParams(nameOrOptions, maybeOptions); return target => { @@ -15,6 +17,7 @@ export function InputType( name: name || target.name, target, description: options.description, + isAbstract: options.isAbstract, }); }; } diff --git a/src/decorators/InterfaceType.ts b/src/decorators/InterfaceType.ts index 20f69db8b..5befd5e36 100644 --- a/src/decorators/InterfaceType.ts +++ b/src/decorators/InterfaceType.ts @@ -1,13 +1,15 @@ import { getMetadataStorage } from "../metadata/getMetadataStorage"; import { getNameDecoratorParams } from "../helpers/decorators"; -import { DescriptionOptions } from "./types"; +import { DescriptionOptions, AbstractClassOptions } from "./types"; + +export type InterfaceOptions = DescriptionOptions & AbstractClassOptions; export function InterfaceType(): ClassDecorator; -export function InterfaceType(options: DescriptionOptions): ClassDecorator; -export function InterfaceType(name: string, options?: DescriptionOptions): ClassDecorator; +export function InterfaceType(options: InterfaceOptions): ClassDecorator; +export function InterfaceType(name: string, options?: InterfaceOptions): ClassDecorator; export function InterfaceType( - nameOrOptions?: string | DescriptionOptions, - maybeOptions?: DescriptionOptions, + nameOrOptions?: string | InterfaceOptions, + maybeOptions?: InterfaceOptions, ): ClassDecorator { const { name, options } = getNameDecoratorParams(nameOrOptions, maybeOptions); return target => { @@ -15,6 +17,7 @@ export function InterfaceType( name: name || target.name, target, description: options.description, + isAbstract: options.isAbstract, }); }; } diff --git a/src/decorators/ObjectType.ts b/src/decorators/ObjectType.ts index e0f5b262f..d8bb82953 100644 --- a/src/decorators/ObjectType.ts +++ b/src/decorators/ObjectType.ts @@ -1,11 +1,12 @@ import { getMetadataStorage } from "../metadata/getMetadataStorage"; import { getNameDecoratorParams } from "../helpers/decorators"; -import { DescriptionOptions } from "./types"; +import { DescriptionOptions, AbstractClassOptions } from "./types"; import { ClassType } from "../interfaces"; -export type ObjectOptions = DescriptionOptions & { - implements?: Function | Function[]; -}; +export type ObjectOptions = DescriptionOptions & + AbstractClassOptions & { + implements?: Function | Function[]; + }; export function ObjectType(): ClassDecorator; export function ObjectType(options: ObjectOptions): ClassDecorator; @@ -24,6 +25,7 @@ export function ObjectType( target, description: options.description, interfaceClasses, + isAbstract: options.isAbstract, }); }; } diff --git a/src/decorators/types.ts b/src/decorators/types.ts index 9a6ec1645..89e998ab0 100644 --- a/src/decorators/types.ts +++ b/src/decorators/types.ts @@ -44,6 +44,9 @@ export interface ComplexityOptions { export interface SchemaNameOptions { name?: string; } +export interface AbstractClassOptions { + isAbstract?: boolean; +} export type BasicOptions = DecoratorTypeOptions & DescriptionOptions; export type AdvancedOptions = BasicOptions & DepreciationOptions & @@ -57,6 +60,4 @@ export interface EnumConfig { export type MethodAndPropDecorator = PropertyDecorator & MethodDecorator; -export interface ResolverClassOptions { - isAbstract?: boolean; -} +export type ResolverClassOptions = AbstractClassOptions; diff --git a/src/metadata/definitions/class-metadata.ts b/src/metadata/definitions/class-metadata.ts index fe42f793a..358da6eb6 100644 --- a/src/metadata/definitions/class-metadata.ts +++ b/src/metadata/definitions/class-metadata.ts @@ -6,4 +6,5 @@ export interface ClassMetadata { fields?: FieldMetadata[]; description?: string; interfaceClasses?: Function[]; + isAbstract?: boolean; } diff --git a/src/schema/schema-generator.ts b/src/schema/schema-generator.ts index ef27df101..2d0d6093a 100644 --- a/src/schema/schema-generator.ts +++ b/src/schema/schema-generator.ts @@ -42,17 +42,20 @@ import { ResolverFilterData, ResolverTopicData } from "../interfaces"; import { getFieldMetadataFromInputType, getFieldMetadataFromObjectType } from "./utils"; import { ensureInstalledCorrectGraphQLPackage } from "../utils/graphql-version"; -interface ObjectTypeInfo { +interface AbstractInfo { + isAbstract: boolean; +} +interface ObjectTypeInfo extends AbstractInfo { target: Function; type: GraphQLObjectType; } -interface InputObjectTypeInfo { +interface InterfaceTypeInfo extends AbstractInfo { target: Function; - type: GraphQLInputObjectType; + type: GraphQLInterfaceType; } -interface InterfaceTypeInfo { +interface InputObjectTypeInfo extends AbstractInfo { target: Function; - type: GraphQLInterfaceType; + type: GraphQLInputObjectType; } interface EnumTypeInfo { enumObj: object; @@ -181,6 +184,7 @@ export abstract class SchemaGenerator { }; return { target: interfaceType.target, + isAbstract: interfaceType.isAbstract || false, type: new GraphQLInterfaceType({ name: interfaceType.name, description: interfaceType.description, @@ -222,6 +226,7 @@ export abstract class SchemaGenerator { const interfaceClasses = objectType.interfaceClasses || []; return { target: objectType.target, + isAbstract: objectType.isAbstract || false, type: new GraphQLObjectType({ name: objectType.name, description: objectType.description, @@ -306,6 +311,7 @@ export abstract class SchemaGenerator { const inputInstance = new (inputType.target as any)(); return { target: inputType.target, + isAbstract: inputType.isAbstract || false, type: new GraphQLInputObjectType({ name: inputType.name, description: inputType.description, @@ -374,8 +380,9 @@ export abstract class SchemaGenerator { // TODO: investigate the need of directly providing this types // maybe GraphQL can use only the types provided indirectly return [ - ...this.objectTypesInfo.map(it => it.type), - ...this.interfaceTypesInfo.map(it => it.type), + ...this.objectTypesInfo.filter(it => !it.isAbstract).map(it => it.type), + ...this.interfaceTypesInfo.filter(it => !it.isAbstract).map(it => it.type), + ...this.inputTypesInfo.filter(it => !it.isAbstract).map(it => it.type), ]; }