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
This commit is contained in:
Alex Rickabaugh 2019-03-01 11:40:42 -08:00 committed by Andrew Kushnir
parent 3e5c1bcb9f
commit b1df9a30f4
2 changed files with 26 additions and 8 deletions

View File

@ -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) {

View File

@ -160,6 +160,24 @@ describe('ngtsc behavioral tests', () => {
expect(dtsContents).toContain('static ngInjectableDef: i0.ɵInjectableDef<Service>;');
});
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', `