From 0db1b5d8f11b23d29ee1a4f2226cb95ef8748bc0 Mon Sep 17 00:00:00 2001 From: JoostK Date: Sun, 9 Jun 2019 20:12:27 +0200 Subject: [PATCH] fix(ivy): handle empty bindings in template type checker (#31594) When a template contains a binding without a value, the template parser creates an `EmptyExpr` node. This would previously be translated into an `undefined` value, which would cause a crash downstream as `undefined` is not included in the allowed type, so it was not handled properly. This commit prevents the crash by returning an actual expression for empty bindings. Fixes #30076 Fixes #30929 PR Close #31594 --- .../compiler-cli/src/ngtsc/typecheck/src/expression.ts | 7 ++++++- .../src/ngtsc/typecheck/test/type_check_block_spec.ts | 10 ++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/expression.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/expression.ts index e9b3973012..7839a77dda 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/expression.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/expression.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {AST, ASTWithSource, AstVisitor, Binary, BindingPipe, Chain, Conditional, FunctionCall, ImplicitReceiver, Interpolation, KeyedRead, KeyedWrite, LiteralArray, LiteralMap, LiteralPrimitive, MethodCall, NonNullAssert, ParseSpan, PrefixNot, PropertyRead, PropertyWrite, Quote, SafeMethodCall, SafePropertyRead} from '@angular/compiler'; +import {AST, ASTWithSource, AstVisitor, Binary, BindingPipe, Chain, Conditional, EmptyExpr, FunctionCall, ImplicitReceiver, Interpolation, KeyedRead, KeyedWrite, LiteralArray, LiteralMap, LiteralPrimitive, MethodCall, NonNullAssert, ParseSpan, PrefixNot, PropertyRead, PropertyWrite, Quote, SafeMethodCall, SafePropertyRead} from '@angular/compiler'; import * as ts from 'typescript'; import {TypeCheckingConfig} from './api'; @@ -60,6 +60,11 @@ class AstTranslator implements AstVisitor { ast = ast.ast; } + // The `EmptyExpr` doesn't have a dedicated method on `AstVisitor`, so it's special cased here. + if (ast instanceof EmptyExpr) { + return UNDEFINED; + } + // First attempt to let any custom resolution logic provide a translation for the given node. const resolved = this.maybeResolve(ast); if (resolved !== null) { diff --git a/packages/compiler-cli/src/ngtsc/typecheck/test/type_check_block_spec.ts b/packages/compiler-cli/src/ngtsc/typecheck/test/type_check_block_spec.ts index 1d6848e6c3..103081c83b 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/test/type_check_block_spec.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/test/type_check_block_spec.ts @@ -39,6 +39,16 @@ describe('type check blocks', () => { expect(tcb(TEMPLATE)).toContain('_t1.htmlFor = ("test");'); }); + it('should handle empty bindings', () => { + const TEMPLATE = ``; + expect(tcb(TEMPLATE)).toContain('_t1.type = (undefined);'); + }); + + it('should handle bindings without value', () => { + const TEMPLATE = ``; + expect(tcb(TEMPLATE)).toContain('_t1.type = (undefined);'); + }); + it('should handle implicit vars on ng-template', () => { const TEMPLATE = ``; expect(tcb(TEMPLATE)).toContain('var _t2 = _t1.$implicit;');