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
This commit is contained in:
JoostK 2019-06-09 20:12:27 +02:00 committed by Andrew Kushnir
parent 8e1a725462
commit 0db1b5d8f1
2 changed files with 16 additions and 1 deletions

View File

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

View File

@ -39,6 +39,16 @@ describe('type check blocks', () => {
expect(tcb(TEMPLATE)).toContain('_t1.htmlFor = ("test");');
});
it('should handle empty bindings', () => {
const TEMPLATE = `<input [type]="">`;
expect(tcb(TEMPLATE)).toContain('_t1.type = (undefined);');
});
it('should handle bindings without value', () => {
const TEMPLATE = `<input [type]>`;
expect(tcb(TEMPLATE)).toContain('_t1.type = (undefined);');
});
it('should handle implicit vars on ng-template', () => {
const TEMPLATE = `<ng-template let-a></ng-template>`;
expect(tcb(TEMPLATE)).toContain('var _t2 = _t1.$implicit;');