From 139846f07069213860a62c9455a72611f26e82c4 Mon Sep 17 00:00:00 2001 From: Vittorio Date: Tue, 31 Dec 2019 07:54:21 +0000 Subject: [PATCH] feat(genericDefault): add support for default generics on declaration and extensions (#126) * add support for default generics on declaration and extensions * reduce amount of nested if statements * reduce amount of if statement in generic extensions --- src/transformer/descriptor/helper/helper.ts | 2 +- .../genericDeclaration/genericDeclaration.ts | 63 ++++++++++++------- .../mockFactoryCall/mockFactoryCall.ts | 26 +++----- .../descriptor/generic/default.test.ts | 49 +++++++++++++++ .../generic/{import.ts => import.test.ts} | 0 5 files changed, 101 insertions(+), 39 deletions(-) create mode 100644 test/transformer/descriptor/generic/default.test.ts rename test/transformer/descriptor/generic/{import.ts => import.test.ts} (100%) diff --git a/src/transformer/descriptor/helper/helper.ts b/src/transformer/descriptor/helper/helper.ts index cf09c0b83..08eb237e6 100644 --- a/src/transformer/descriptor/helper/helper.ts +++ b/src/transformer/descriptor/helper/helper.ts @@ -47,7 +47,7 @@ export namespace TypescriptHelper { const typeDeclaration: ts.Declaration = ts.getTypeParameterOwner(declaration); // THIS IS TO FIX A MISSING IMPLEMENTATION IN TYPESCRIPT https://github.com/microsoft/TypeScript/blob/ba5e86f1406f39e89d56d4b32fd6ff8de09a0bf3/src/compiler/utilities.ts#L5138 - if ((typeDeclaration as Declaration).typeParameters) { + if (typeDeclaration && (typeDeclaration as Declaration).typeParameters) { return typeDeclaration; } diff --git a/src/transformer/genericDeclaration/genericDeclaration.ts b/src/transformer/genericDeclaration/genericDeclaration.ts index ee34c1e2f..481ce9070 100644 --- a/src/transformer/genericDeclaration/genericDeclaration.ts +++ b/src/transformer/genericDeclaration/genericDeclaration.ts @@ -11,6 +11,14 @@ import { GenericParameter } from './genericParameter'; export function GenericDeclaration(scope: Scope): IGenericDeclaration { const generics: GenericParameter[] = []; + function isGenericProvided(node: ts.TypeReferenceNode | ts.ExpressionWithTypeArguments, index: number): boolean { + return !!node.typeArguments && !!node.typeArguments[index]; + } + + function getGenericTypeNode(node: ts.TypeReferenceNode | ts.ExpressionWithTypeArguments, nodeDeclaration: ts.TypeParameterDeclaration, index: number): ts.TypeNode { + return isGenericProvided(node, index) ? node.typeArguments[index] : nodeDeclaration.default; + } + function addGenericParameterToExisting( ownerParameterDeclaration: ts.TypeParameterDeclaration, typeParameterDeclaration: ts.TypeParameterDeclaration, @@ -42,11 +50,21 @@ export function GenericDeclaration(scope: Scope): IGenericDeclaration { addFromTypeReferenceNode(node: ts.TypeReferenceNode, declarationKey: string): void { const typeParameterDeclarations: ts.NodeArray = TypescriptHelper.GetParameterOfNode(node.typeName); - node.typeArguments.forEach((argument: ts.TypeNode, index: number) => { - const genericDescriptor: ts.Expression = GetDescriptor(argument, scope); - const genericParameter: GenericParameter = createGenericParameter(declarationKey, typeParameterDeclarations[index], genericDescriptor); + if (!typeParameterDeclarations) { + return; + } + + typeParameterDeclarations.forEach((declaration: ts.TypeParameterDeclaration, index: number) => { + const genericTypeNode: ts.TypeNode = getGenericTypeNode(node, declaration, index); + + const genericParameter: GenericParameter = createGenericParameter( + declarationKey, + typeParameterDeclarations[index], + GetDescriptor(genericTypeNode, scope)); + generics.push(genericParameter); }); + }, addFromDeclarationExtension( declarationKey: string, @@ -55,9 +73,15 @@ export function GenericDeclaration(scope: Scope): IGenericDeclaration { extension: ts.ExpressionWithTypeArguments): void { const extensionDeclarationTypeParameters: ts.NodeArray = extensionDeclaration.typeParameters; - extension.typeArguments.forEach((typeArgument: ts.TypeNode, index: number) => { - if (ts.isTypeReferenceNode(typeArgument)) { - const typeParameterDeclaration: ts.Declaration = TypescriptHelper.GetDeclarationFromNode(typeArgument.typeName); + if (!extensionDeclarationTypeParameters) { + return; + } + + extensionDeclarationTypeParameters.reduce((acc: GenericParameter[], declaration: ts.TypeParameterDeclaration, index: number) => { + const genericTypeNode: ts.TypeNode = getGenericTypeNode(extension, declaration, index); + + if (ts.isTypeReferenceNode(genericTypeNode)) { + const typeParameterDeclaration: ts.Declaration = TypescriptHelper.GetDeclarationFromNode(genericTypeNode.typeName); if (ts.isTypeParameterDeclaration(typeParameterDeclaration)) { addGenericParameterToExisting( extensionDeclarationTypeParameters[index], @@ -65,26 +89,21 @@ export function GenericDeclaration(scope: Scope): IGenericDeclaration { declarationKey, extensionDeclarationKey, ); - } else { - const genericParameter: GenericParameter = createGenericParameter( - extensionDeclarationKey, - extensionDeclarationTypeParameters[index], - GetDescriptor(typeArgument, scope), - ); - generics.push(genericParameter); + return acc; } + } - } else { - const genericParameter: GenericParameter = createGenericParameter( - extensionDeclarationKey, - extensionDeclarationTypeParameters[index], - GetDescriptor(typeArgument, scope), - ); + const genericParameter: GenericParameter = createGenericParameter( + extensionDeclarationKey, + extensionDeclarationTypeParameters[index], + GetDescriptor(genericTypeNode, scope), + ); - generics.push(genericParameter); - } - }); + acc.push(genericParameter); + + return acc; + }, generics); }, getExpressionForAllGenerics(): ts.ObjectLiteralExpression[] { return generics.map((s: GenericParameter) => { diff --git a/src/transformer/mockFactoryCall/mockFactoryCall.ts b/src/transformer/mockFactoryCall/mockFactoryCall.ts index 6be1a385c..b04da3378 100644 --- a/src/transformer/mockFactoryCall/mockFactoryCall.ts +++ b/src/transformer/mockFactoryCall/mockFactoryCall.ts @@ -28,9 +28,7 @@ export function GetMockFactoryCallIntersection(intersection: ts.IntersectionType const declaration: ts.Declaration = TypescriptHelper.GetDeclarationFromNode((type as ts.TypeReferenceNode).typeName); const declarationKey: string = MockDefiner.instance.getDeclarationKeyMap(declaration); - if (type.typeArguments) { - genericDeclaration.addFromTypeReferenceNode(type, declarationKey); - } + genericDeclaration.addFromTypeReferenceNode(type, declarationKey); addFromDeclarationExtensions(declaration as GenericDeclarationSupported, declarationKey, genericDeclaration); @@ -74,9 +72,7 @@ function getDeclarationMockFactoryCall(declaration: ts.Declaration, typeReferenc const mockFactoryCall: ts.Expression = MockDefiner.instance.getMockFactoryByKey(declarationKey); const genericDeclaration: IGenericDeclaration = GenericDeclaration(scope); - if (typeReferenceNode.typeArguments) { - genericDeclaration.addFromTypeReferenceNode(typeReferenceNode, declarationKey); - } + genericDeclaration.addFromTypeReferenceNode(typeReferenceNode, declarationKey); addFromDeclarationExtensions(declaration as GenericDeclarationSupported, declarationKey, genericDeclaration); @@ -93,19 +89,17 @@ function addFromDeclarationExtensions(declaration: GenericDeclarationSupported, if (declaration.heritageClauses) { declaration.heritageClauses.forEach((clause: ts.HeritageClause) => { clause.types.forEach((extension: ts.ExpressionWithTypeArguments) => { - if (extension.typeArguments) { - const extensionDeclaration: ts.Declaration = TypescriptHelper.GetDeclarationFromNode(extension.expression); + const extensionDeclaration: ts.Declaration = TypescriptHelper.GetDeclarationFromNode(extension.expression); - const extensionDeclarationKey: string = MockDefiner.instance.getDeclarationKeyMap(extensionDeclaration); + const extensionDeclarationKey: string = MockDefiner.instance.getDeclarationKeyMap(extensionDeclaration); - genericDeclaration.addFromDeclarationExtension( - declarationKey, - extensionDeclaration as GenericDeclarationSupported, - extensionDeclarationKey, - extension); + genericDeclaration.addFromDeclarationExtension( + declarationKey, + extensionDeclaration as GenericDeclarationSupported, + extensionDeclarationKey, + extension); - addFromDeclarationExtensions(extensionDeclaration as GenericDeclarationSupported, extensionDeclarationKey, genericDeclaration); - } + addFromDeclarationExtensions(extensionDeclaration as GenericDeclarationSupported, extensionDeclarationKey, genericDeclaration); }); }); } diff --git a/test/transformer/descriptor/generic/default.test.ts b/test/transformer/descriptor/generic/default.test.ts new file mode 100644 index 000000000..1038febdd --- /dev/null +++ b/test/transformer/descriptor/generic/default.test.ts @@ -0,0 +1,49 @@ +import { createMock } from 'ts-auto-mock'; + +describe('generic default', () => { + it('should assign the default value when not provided', () => { + interface B

{ + prop: P; + } + + interface A

extends B

{ + } + + const mock: A = createMock(); + + expect(mock.prop.a).toEqual(''); + }); + + it('should assign the default value of the second argument when not provided', () => { + interface B { + prop: P; + prop2: S; + } + + interface A

extends B { + } + + const mock: A<{ a: number }> = createMock>(); + + expect(mock.prop.a).toEqual(0); + expect(mock.prop2).toEqual(0); + }); + + it('should assign the default value for extension with default value', () => { + interface C { + cProp: T; + } + + interface B

extends C

{ + bProp: P; + } + + interface A extends B { + } + + const mock: A = createMock(); + + expect(mock.cProp.a).toEqual(''); + expect(mock.bProp.a).toEqual(''); + }); +}); diff --git a/test/transformer/descriptor/generic/import.ts b/test/transformer/descriptor/generic/import.test.ts similarity index 100% rename from test/transformer/descriptor/generic/import.ts rename to test/transformer/descriptor/generic/import.test.ts