From b1df9a30f40aa9474ac78d541e76f8a13170be2d Mon Sep 17 00:00:00 2001 From: Alex Rickabaugh Date: Fri, 1 Mar 2019 11:40:42 -0800 Subject: [PATCH] fix(ivy): use the imported name of decorators for detection (#29061) Currently, ngtsc has a bug where if you alias the name of a decorator when importing it, it won't be detected properly. This is because the compiler uses the aliased name and not the original, declared name of the decorator for detection. This commit fixes the compiler to compare against the declared name of decorators when available, and adds a test to prevent regression. PR Close #29061 --- .../src/ngtsc/annotations/src/util.ts | 16 ++++++++-------- packages/compiler-cli/test/ngtsc/ngtsc_spec.ts | 18 ++++++++++++++++++ 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/packages/compiler-cli/src/ngtsc/annotations/src/util.ts b/packages/compiler-cli/src/ngtsc/annotations/src/util.ts index 002a5803d7..a10ff41b54 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/src/util.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/src/util.ts @@ -49,22 +49,23 @@ export function getConstructorDependencies( let optional = false, self = false, skipSelf = false, host = false; let resolved = R3ResolvedDependencyType.Token; (param.decorators || []).filter(dec => isCore || isAngularCore(dec)).forEach(dec => { - if (dec.name === 'Inject') { + const name = isCore || dec.import === null ? dec.name : dec.import !.name; + if (name === 'Inject') { if (dec.args === null || dec.args.length !== 1) { throw new FatalDiagnosticError( ErrorCode.DECORATOR_ARITY_WRONG, dec.node, `Unexpected number of arguments to @Inject().`); } tokenExpr = dec.args[0]; - } else if (dec.name === 'Optional') { + } else if (name === 'Optional') { optional = true; - } else if (dec.name === 'SkipSelf') { + } else if (name === 'SkipSelf') { skipSelf = true; - } else if (dec.name === 'Self') { + } else if (name === 'Self') { self = true; - } else if (dec.name === 'Host') { + } else if (name === 'Host') { host = true; - } else if (dec.name === 'Attribute') { + } else if (name === 'Attribute') { if (dec.args === null || dec.args.length !== 1) { throw new FatalDiagnosticError( ErrorCode.DECORATOR_ARITY_WRONG, dec.node, @@ -74,8 +75,7 @@ export function getConstructorDependencies( resolved = R3ResolvedDependencyType.Attribute; } else { throw new FatalDiagnosticError( - ErrorCode.DECORATOR_UNEXPECTED, dec.node, - `Unexpected decorator ${dec.name} on parameter.`); + ErrorCode.DECORATOR_UNEXPECTED, dec.node, `Unexpected decorator ${name} on parameter.`); } }); if (tokenExpr === null) { diff --git a/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts b/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts index 9c2c20c57e..1e50aab87d 100644 --- a/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts +++ b/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts @@ -160,6 +160,24 @@ describe('ngtsc behavioral tests', () => { expect(dtsContents).toContain('static ngInjectableDef: i0.ɵInjectableDef;'); }); + it('should compile @Injectable with an @Optional dependency', () => { + env.tsconfig(); + env.write('test.ts', ` + import {Injectable, Optional as Opt} from '@angular/core'; + + @Injectable() + class Dep {} + + @Injectable() + class Service { + constructor(@Opt() dep: Dep) {} + } + `); + env.driveMain(); + const jsContents = env.getContents('test.js'); + expect(jsContents).toContain('inject(Dep, 8)'); + }); + it('should compile Components (inline template) without errors', () => { env.tsconfig(); env.write('test.ts', `