fix(ivy): include type parameter for `ngBaseDef` declaration (#31210)

When a class uses Angular decorators such as `@Input`, `@Output` and
friends without an Angular class decorator, they are compiled into a
static `ngBaseDef` field on the class, with the TypeScript declaration
of the class being altered to declare the `ngBaseDef` field to be of type
`ɵɵBaseDef`. This type however requires a generic type parameter that
corresponds with the type of the class, however the compiler did not
provide this type parameter. As a result, compiling a program where such
invalid `ngBaseDef` declarations are present will result in compilation
errors.

This commit fixes the problem by providing the generic type parameter.

Fixes #31160

PR Close #31210
This commit is contained in:
JoostK 2019-06-22 17:22:50 +02:00 committed by Alex Rickabaugh
parent a4a423a083
commit eb6281f5b4
6 changed files with 30 additions and 4 deletions

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {ConstantPool, R3BaseRefMetaData, compileBaseDefFromMetadata, makeBindingParser} from '@angular/compiler';
import {ConstantPool, R3BaseRefMetaData, WrappedNodeExpr, compileBaseDefFromMetadata, makeBindingParser} from '@angular/compiler';
import {PartialEvaluator} from '../../partial_evaluator';
import {ClassDeclaration, ClassMember, Decorator, ReflectionHost} from '../../reflection';
@ -91,7 +91,11 @@ export class BaseDefDecoratorHandler implements
analyze(node: ClassDeclaration, metadata: R3BaseRefDecoratorDetection):
AnalysisOutput<R3BaseRefMetaData> {
const analysis: R3BaseRefMetaData = {name: node.name.text, typeSourceSpan: null !};
const analysis: R3BaseRefMetaData = {
name: node.name.text,
type: new WrappedNodeExpr(node.name),
typeSourceSpan: null !
};
if (metadata.inputs) {
const inputs = analysis.inputs = {} as{[key: string]: string | [string, string]};

View File

@ -423,6 +423,24 @@ runInEachFileSystem(os => {
expect(jsContents).toContain('background-color: blue');
});
it('should include generic type for ngBaseDef declarations', () => {
env.write('test.ts', `
import {Component, Input, NgModule} from '@angular/core';
export class TestBase {
@Input() input: any;
}
`);
env.driveMain();
const jsContents = env.getContents('test.js');
expect(jsContents).toContain('i0.ɵɵdefineBase({ inputs: { input: "input" } });');
const dtsContents = env.getContents('test.d.ts');
expect(dtsContents).toContain('static ngBaseDef: i0.ɵɵBaseDef<TestBase>');
});
it('should compile NgModules without errors', () => {
env.write('test.ts', `
import {Component, NgModule} from '@angular/core';

View File

@ -151,6 +151,7 @@ export interface R3ComponentMetadataFacade extends R3DirectiveMetadataFacade {
export interface R3BaseMetadataFacade {
name: string;
type: any;
propMetadata: {[key: string]: any[]};
inputs?: {[key: string]: string | [string, string]};
outputs?: {[key: string]: string};

View File

@ -142,6 +142,7 @@ export function compileDirectiveFromMetadata(
export interface R3BaseRefMetaData {
name: string;
type: o.Expression;
typeSourceSpan: ParseSourceSpan;
inputs?: {[key: string]: string | [string, string]};
outputs?: {[key: string]: string};
@ -188,7 +189,8 @@ export function compileBaseDefFromMetadata(
}
const expression = o.importExpr(R3.defineBase).callFn([definitionMap.toLiteralMap()]);
const type = new o.ExpressionType(o.importExpr(R3.BaseDef));
const type = new o.ExpressionType(
o.importExpr(R3.BaseDef), /* modifiers */ null, [o.expressionType(meta.type)]);
return {expression, type};
}

View File

@ -151,6 +151,7 @@ export interface R3ComponentMetadataFacade extends R3DirectiveMetadataFacade {
export interface R3BaseMetadataFacade {
name: string;
type: any;
propMetadata: {[key: string]: any[]};
inputs?: {[key: string]: string | [string, string]};
outputs?: {[key: string]: string};

View File

@ -241,7 +241,7 @@ function extractBaseDefMetadata(type: Type<any>): R3BaseMetadataFacade|null {
// Only generate the base def if there's any info inside it.
if (inputs || outputs || viewQueries.length || queries.length || hasHostDecorators) {
return {name: type.name, inputs, outputs, viewQueries, queries, propMetadata};
return {name: type.name, type, inputs, outputs, viewQueries, queries, propMetadata};
}
return null;