From eb6281f5b471e55d5c761065e17ab2a293690d29 Mon Sep 17 00:00:00 2001 From: JoostK Date: Sat, 22 Jun 2019 17:22:50 +0200 Subject: [PATCH] fix(ivy): include type parameter for `ngBaseDef` declaration (#31210) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- .../src/ngtsc/annotations/src/base_def.ts | 8 ++++++-- packages/compiler-cli/test/ngtsc/ngtsc_spec.ts | 18 ++++++++++++++++++ .../compiler/src/compiler_facade_interface.ts | 1 + packages/compiler/src/render3/view/compiler.ts | 4 +++- .../src/compiler/compiler_facade_interface.ts | 1 + packages/core/src/render3/jit/directive.ts | 2 +- 6 files changed, 30 insertions(+), 4 deletions(-) diff --git a/packages/compiler-cli/src/ngtsc/annotations/src/base_def.ts b/packages/compiler-cli/src/ngtsc/annotations/src/base_def.ts index ce23c170a8..2fb78c0d93 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/src/base_def.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/src/base_def.ts @@ -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 { - 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]}; diff --git a/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts b/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts index e6bac33ec2..6699aa813c 100644 --- a/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts +++ b/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts @@ -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'); + }); + it('should compile NgModules without errors', () => { env.write('test.ts', ` import {Component, NgModule} from '@angular/core'; diff --git a/packages/compiler/src/compiler_facade_interface.ts b/packages/compiler/src/compiler_facade_interface.ts index 8fb8a44d07..1564d28fe3 100644 --- a/packages/compiler/src/compiler_facade_interface.ts +++ b/packages/compiler/src/compiler_facade_interface.ts @@ -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}; diff --git a/packages/compiler/src/render3/view/compiler.ts b/packages/compiler/src/render3/view/compiler.ts index 3b364617f1..f2efe3a0bd 100644 --- a/packages/compiler/src/render3/view/compiler.ts +++ b/packages/compiler/src/render3/view/compiler.ts @@ -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}; } diff --git a/packages/core/src/compiler/compiler_facade_interface.ts b/packages/core/src/compiler/compiler_facade_interface.ts index 8fb8a44d07..1564d28fe3 100644 --- a/packages/core/src/compiler/compiler_facade_interface.ts +++ b/packages/core/src/compiler/compiler_facade_interface.ts @@ -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}; diff --git a/packages/core/src/render3/jit/directive.ts b/packages/core/src/render3/jit/directive.ts index 5f7dd714e3..0ba5ca7296 100644 --- a/packages/core/src/render3/jit/directive.ts +++ b/packages/core/src/render3/jit/directive.ts @@ -241,7 +241,7 @@ function extractBaseDefMetadata(type: Type): 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;