From 1651a8f18966e26fad8a3e98308e22e1529e26de Mon Sep 17 00:00:00 2001 From: Chuck Jazdzewski Date: Thu, 20 Apr 2017 17:34:44 -0700 Subject: [PATCH] fix(tsc-wrapped): ignore `|null` and `|undefined` when collecting types (#16222) --- packages/compiler/src/aot/static_reflector.ts | 1 - tools/@angular/tsc-wrapped/src/evaluator.ts | 30 +++++++++++++++++++ .../tsc-wrapped/test/collector.spec.ts | 24 +++++++++++++++ 3 files changed, 54 insertions(+), 1 deletion(-) diff --git a/packages/compiler/src/aot/static_reflector.ts b/packages/compiler/src/aot/static_reflector.ts index fbd0ed5292..a3310f10e9 100644 --- a/packages/compiler/src/aot/static_reflector.ts +++ b/packages/compiler/src/aot/static_reflector.ts @@ -191,7 +191,6 @@ export class StaticReflector implements CompileReflector { if (paramType) nestedResult.push(paramType); const decorators = parameterDecorators ? parameterDecorators[index] : null; if (decorators) { - if (paramType) nestedResult.push(paramType); nestedResult.push(...decorators); } parameters !.push(nestedResult); diff --git a/tools/@angular/tsc-wrapped/src/evaluator.ts b/tools/@angular/tsc-wrapped/src/evaluator.ts index 9e4d418ef0..1559cb186e 100644 --- a/tools/@angular/tsc-wrapped/src/evaluator.ts +++ b/tools/@angular/tsc-wrapped/src/evaluator.ts @@ -442,6 +442,36 @@ export class Evaluator { (typeReference).arguments = args; } return recordEntry(typeReference, node); + case ts.SyntaxKind.UnionType: + const unionType = node; + + // Remove null and undefined from the list of unions. + const references = unionType.types + .filter( + n => n.kind != ts.SyntaxKind.NullKeyword && + n.kind != ts.SyntaxKind.UndefinedKeyword) + .map(n => this.evaluateNode(n)); + + // The remmaining reference must be the same. If two have type arguments consider them + // different even if the type arguments are the same. + let candidate: any = null; + for (let i = 0; i < references.length; i++) { + const reference = references[i]; + if (isMetadataSymbolicReferenceExpression(reference)) { + if (candidate) { + if ((reference as any).name == candidate.name && + (reference as any).module == candidate.module && !(reference as any).arguments) { + candidate = reference; + } + } else { + candidate = reference; + } + } else { + return reference; + } + } + if (candidate) return candidate; + break; case ts.SyntaxKind.NoSubstitutionTemplateLiteral: case ts.SyntaxKind.StringLiteral: case ts.SyntaxKind.TemplateHead: diff --git a/tools/@angular/tsc-wrapped/test/collector.spec.ts b/tools/@angular/tsc-wrapped/test/collector.spec.ts index 0dddee2509..89e8caee87 100644 --- a/tools/@angular/tsc-wrapped/test/collector.spec.ts +++ b/tools/@angular/tsc-wrapped/test/collector.spec.ts @@ -707,6 +707,30 @@ describe('Collector', () => { }); }); + it('should ignore |null or |undefined in type expressions', () => { + const source = ts.createSourceFile( + 'somefile.ts', ` + import {Foo} from './foo'; + export class SomeClass { + constructor (a: Foo, b: Foo | null, c: Foo | undefined, d: Foo | undefined | null, e: Foo | undefined | null | Foo) {} + } + `, + ts.ScriptTarget.Latest, true); + const metadata = collector.getMetadata(source); + expect((metadata.metadata['SomeClass'] as ClassMetadata).members).toEqual({ + __ctor__: [{ + __symbolic: 'constructor', + parameters: [ + {__symbolic: 'reference', module: './foo', name: 'Foo'}, + {__symbolic: 'reference', module: './foo', name: 'Foo'}, + {__symbolic: 'reference', module: './foo', name: 'Foo'}, + {__symbolic: 'reference', module: './foo', name: 'Foo'}, + {__symbolic: 'reference', module: './foo', name: 'Foo'} + ] + }] + }); + }); + describe('in strict mode', () => { it('should throw if an error symbol is collecting a reference to a non-exported symbol', () => { const source = program.getSourceFile('/local-symbol-ref.ts');