From 444bbfef23b1c0fa45679bcbfba1cfa6df5ee45e Mon Sep 17 00:00:00 2001 From: Alan Agius Date: Tue, 25 Feb 2020 11:29:11 +0100 Subject: [PATCH] fix(@ngtools/webpack): handle union type with a nullable argument Currently constructor parameters with union types that contains nullable argument are not being converted properly and result in broken behaviour. With this change we align the ctor-parameters downlevel transformer to be closer to the NGTSC reflector: https://github.com/angular/angular/blob/master/packages/compiler-cli/src/ngtsc/reflection/src/typescript.ts#L65-L66 This change should also be synced to ng-packagr. Closes: #17063 Reference: FW-1883 --- .../src/transformers/ctor-parameters.ts | 7 ++++ .../src/transformers/ctor-parameters_spec.ts | 37 +++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/packages/ngtools/webpack/src/transformers/ctor-parameters.ts b/packages/ngtools/webpack/src/transformers/ctor-parameters.ts index e58c6a458b79..e0df7501f51f 100644 --- a/packages/ngtools/webpack/src/transformers/ctor-parameters.ts +++ b/packages/ngtools/webpack/src/transformers/ctor-parameters.ts @@ -174,6 +174,13 @@ function typeReferenceToExpression( case ts.SyntaxKind.NumberKeyword: case ts.SyntaxKind.NumericLiteral: return ts.createIdentifier('Number'); + case ts.SyntaxKind.UnionType: + const childTypeNodes = (node as ts.UnionTypeNode).types.filter(t => t.kind !== ts.SyntaxKind.NullKeyword); + + return childTypeNodes.length === 1 + ? typeReferenceToExpression(entityNameToExpression, childTypeNodes[0], typeChecker) + : undefined; + case ts.SyntaxKind.TypeReference: const typeRef = node as ts.TypeReferenceNode; let typeSymbol = typeChecker.getSymbolAtLocation(typeRef.typeName); diff --git a/packages/ngtools/webpack/src/transformers/ctor-parameters_spec.ts b/packages/ngtools/webpack/src/transformers/ctor-parameters_spec.ts index 7c868621b27c..bf1b1a879ddb 100644 --- a/packages/ngtools/webpack/src/transformers/ctor-parameters_spec.ts +++ b/packages/ngtools/webpack/src/transformers/ctor-parameters_spec.ts @@ -17,6 +17,7 @@ function transform(input: string, additionalFiles?: Record) { return result; } +// tslint:disable-next-line: no-big-function describe('Constructor Parameter Transformer', () => { it('records class name in same module', () => { const input = ` @@ -211,4 +212,40 @@ describe('Constructor Parameter Transformer', () => { expect(tags.oneLine`${result}`).toEqual(tags.oneLine`${output}`); }); + + it('should work with union type and nullable argument', () => { + const input = ` + @Injectable() + export class ProvidedService { + constructor() { } + } + + @Injectable() + export class LibService { + constructor( + @Optional() private service: ProvidedService | null, + ) { + } + } + `; + + const output = ` + import { __decorate, __param } from "tslib"; + + let ProvidedService = class ProvidedService { constructor() { } }; + ProvidedService = __decorate([ Injectable() ], ProvidedService); + export { ProvidedService }; + + let LibService = class LibService { + constructor(service) { this.service = service; } + }; + LibService.ctorParameters = () => [ { type: ProvidedService, decorators: [{ type: Optional }] } ]; + LibService = __decorate([ Injectable(), __param(0, Optional()) ], LibService); + export { LibService }; + `; + + const result = transform(input); + + expect(tags.oneLine`${result}`).toEqual(tags.oneLine`${output}`); + }); });