Skip to content

Commit 44a49a6

Browse files
committed
fix(inheritance): fix extending non-TypeGraphQL classes
1 parent ec0f01c commit 44a49a6

3 files changed

Lines changed: 70 additions & 21 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Changelog and release notes
22

3+
## Unreleased
4+
### Fixes
5+
- fix bug with extending non-TypeGraphQL classes
6+
37
## v0.9.0
48
### Features
59
- add support for GraphQL subscriptions using `graphql-subscriptions`

src/schema/schema-generator.ts

Lines changed: 39 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -141,8 +141,12 @@ export abstract class SchemaGenerator {
141141
interfaceType => {
142142
const interfaceSuperClass = Object.getPrototypeOf(interfaceType.target);
143143
const hasExtended = interfaceSuperClass.prototype !== undefined;
144-
const getSuperClassType = () =>
145-
this.interfaceTypesInfo.find(type => type.target === interfaceSuperClass)!.type;
144+
const getSuperClassType = () => {
145+
const superClassTypeInfo = this.interfaceTypesInfo.find(
146+
type => type.target === interfaceSuperClass,
147+
);
148+
return superClassTypeInfo ? superClassTypeInfo.type : undefined;
149+
};
146150
return {
147151
target: interfaceType.target,
148152
type: new GraphQLInterfaceType({
@@ -161,11 +165,11 @@ export abstract class SchemaGenerator {
161165
);
162166
// support for extending interface classes - get field info from prototype
163167
if (hasExtended) {
164-
fields = Object.assign(
165-
{},
166-
this.getFieldMetadataFromObjectType(getSuperClassType()),
167-
fields,
168-
);
168+
const superClass = getSuperClassType();
169+
if (superClass) {
170+
const superClassFields = this.getFieldMetadataFromObjectType(superClass);
171+
fields = Object.assign({}, superClassFields, fields);
172+
}
169173
}
170174
return fields;
171175
},
@@ -177,8 +181,12 @@ export abstract class SchemaGenerator {
177181
this.objectTypesInfo = MetadataStorage.objectTypes.map<ObjectTypeInfo>(objectType => {
178182
const objectSuperClass = Object.getPrototypeOf(objectType.target);
179183
const hasExtended = objectSuperClass.prototype !== undefined;
180-
const getSuperClassType = () =>
181-
this.objectTypesInfo.find(type => type.target === objectSuperClass)!.type;
184+
const getSuperClassType = () => {
185+
const superClassTypeInfo = this.objectTypesInfo.find(
186+
type => type.target === objectSuperClass,
187+
);
188+
return superClassTypeInfo ? superClassTypeInfo.type : undefined;
189+
};
182190
const interfaceClasses = objectType.interfaceClasses || [];
183191
return {
184192
target: objectType.target,
@@ -196,8 +204,11 @@ export abstract class SchemaGenerator {
196204
);
197205
// copy interfaces from super class
198206
if (hasExtended) {
199-
const superInterfaces = getSuperClassType().getInterfaces();
200-
interfaces = Array.from(new Set(interfaces.concat(superInterfaces)));
207+
const superClass = getSuperClassType();
208+
if (superClass) {
209+
const superInterfaces = superClass.getInterfaces();
210+
interfaces = Array.from(new Set(interfaces.concat(superInterfaces)));
211+
}
201212
}
202213
return interfaces;
203214
},
@@ -224,11 +235,11 @@ export abstract class SchemaGenerator {
224235
);
225236
// support for extending classes - get field info from prototype
226237
if (hasExtended) {
227-
fields = Object.assign(
228-
{},
229-
this.getFieldMetadataFromObjectType(getSuperClassType()),
230-
fields,
231-
);
238+
const superClass = getSuperClassType();
239+
if (superClass) {
240+
const superClassFields = this.getFieldMetadataFromObjectType(superClass);
241+
fields = Object.assign({}, superClassFields, fields);
242+
}
232243
}
233244
// support for implicitly implementing interfaces
234245
// get fields from interfaces definitions
@@ -251,15 +262,19 @@ export abstract class SchemaGenerator {
251262

252263
this.inputTypesInfo = MetadataStorage.inputTypes.map<InputObjectTypeInfo>(inputType => {
253264
const objectSuperClass = Object.getPrototypeOf(inputType.target);
254-
const getSuperClassType = () =>
255-
this.inputTypesInfo.find(type => type.target === objectSuperClass)!.type;
265+
const getSuperClassType = () => {
266+
const superClassTypeInfo = this.inputTypesInfo.find(
267+
type => type.target === objectSuperClass,
268+
);
269+
return superClassTypeInfo ? superClassTypeInfo.type : undefined;
270+
};
256271
return {
257272
target: inputType.target,
258273
type: new GraphQLInputObjectType({
259274
name: inputType.name,
260275
description: inputType.description,
261276
fields: () => {
262-
const fields = inputType.fields!.reduce<GraphQLInputFieldConfigMap>(
277+
let fields = inputType.fields!.reduce<GraphQLInputFieldConfigMap>(
263278
(fieldsMap, field) => {
264279
fieldsMap[field.name] = {
265280
description: field.description,
@@ -271,7 +286,11 @@ export abstract class SchemaGenerator {
271286
);
272287
// support for extending classes - get field info from prototype
273288
if (objectSuperClass.prototype !== undefined) {
274-
Object.assign(fields, this.getFieldMetadataFromInputType(getSuperClassType()));
289+
const superClass = getSuperClassType();
290+
if (superClass) {
291+
const superClassFields = this.getFieldMetadataFromInputType(superClass);
292+
fields = Object.assign({}, superClassFields, fields);
293+
}
275294
}
276295
return fields;
277296
},

tests/functional/interfaces-and-inheritance.ts

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ import {
3131
Int,
3232
} from "../../src";
3333

34-
describe("Intefaces and inheritance", () => {
34+
describe("Interfaces and inheritance", () => {
3535
describe("Schema", () => {
3636
let schemaIntrospection: IntrospectionSchema;
3737
let queryType: IntrospectionObjectType;
@@ -457,6 +457,17 @@ describe("Intefaces and inheritance", () => {
457457
@Field() secondField: string;
458458
}
459459

460+
class SampleBaseClass {
461+
static sampleStaticMethod() {
462+
return "sampleStaticMethod";
463+
}
464+
}
465+
466+
@ObjectType()
467+
class SampleExtendingNormalClassObject extends SampleBaseClass {
468+
@Field() sampleField: string;
469+
}
470+
460471
class InterfacesResolver {
461472
@Query()
462473
getInterfacePlainObject(): BaseInterface {
@@ -481,6 +492,11 @@ describe("Intefaces and inheritance", () => {
481492
mutationInput = input;
482493
return true;
483494
}
495+
496+
@Query()
497+
baseClassQuery(): string {
498+
return SampleExtendingNormalClassObject.sampleStaticMethod();
499+
}
484500
}
485501

486502
schema = await buildSchema({
@@ -580,5 +596,15 @@ describe("Intefaces and inheritance", () => {
580596
expect(mutationInput.childInputField).toEqual("childInputField");
581597
expect(mutationInput.optionalBaseInputField).toEqual(255);
582598
});
599+
600+
it("should correctly extends non-TypeGraphQL class", async () => {
601+
const query = `query {
602+
baseClassQuery
603+
}`;
604+
605+
const { data } = await graphql(schema, query);
606+
607+
expect(data!.baseClassQuery).toEqual("sampleStaticMethod");
608+
});
583609
});
584610
});

0 commit comments

Comments
 (0)