feat(compiler): support a non-null postfix assert (#16672)
Template expressions can now use a post-fix `!` operator that asserts the target of the operator is not null. This is similar to the TypeScript non-null assert operator. Expressions generated in factories will be generated with the non-null assert operator. Closes: #10855
This commit is contained in:

committed by
Jason Aden

parent
2eca6e67e1
commit
b9521b568f
@ -166,6 +166,13 @@ export class PrefixNot extends AST {
|
||||
}
|
||||
}
|
||||
|
||||
export class NonNullAssert extends AST {
|
||||
constructor(span: ParseSpan, public expression: AST) { super(span); }
|
||||
visit(visitor: AstVisitor, context: any = null): any {
|
||||
return visitor.visitNonNullAssert(this, context);
|
||||
}
|
||||
}
|
||||
|
||||
export class MethodCall extends AST {
|
||||
constructor(span: ParseSpan, public receiver: AST, public name: string, public args: any[]) {
|
||||
super(span);
|
||||
@ -222,6 +229,7 @@ export interface AstVisitor {
|
||||
visitMethodCall(ast: MethodCall, context: any): any;
|
||||
visitPipe(ast: BindingPipe, context: any): any;
|
||||
visitPrefixNot(ast: PrefixNot, context: any): any;
|
||||
visitNonNullAssert(ast: NonNullAssert, context: any): any;
|
||||
visitPropertyRead(ast: PropertyRead, context: any): any;
|
||||
visitPropertyWrite(ast: PropertyWrite, context: any): any;
|
||||
visitQuote(ast: Quote, context: any): any;
|
||||
@ -230,7 +238,7 @@ export interface AstVisitor {
|
||||
visit?(ast: AST, context?: any): any;
|
||||
}
|
||||
|
||||
export class NullAstVisitor {
|
||||
export class NullAstVisitor implements AstVisitor {
|
||||
visitBinary(ast: Binary, context: any): any {}
|
||||
visitChain(ast: Chain, context: any): any {}
|
||||
visitConditional(ast: Conditional, context: any): any {}
|
||||
@ -245,6 +253,7 @@ export class NullAstVisitor {
|
||||
visitMethodCall(ast: MethodCall, context: any): any {}
|
||||
visitPipe(ast: BindingPipe, context: any): any {}
|
||||
visitPrefixNot(ast: PrefixNot, context: any): any {}
|
||||
visitNonNullAssert(ast: NonNullAssert, context: any): any {}
|
||||
visitPropertyRead(ast: PropertyRead, context: any): any {}
|
||||
visitPropertyWrite(ast: PropertyWrite, context: any): any {}
|
||||
visitQuote(ast: Quote, context: any): any {}
|
||||
@ -303,6 +312,10 @@ export class RecursiveAstVisitor implements AstVisitor {
|
||||
ast.expression.visit(this);
|
||||
return null;
|
||||
}
|
||||
visitNonNullAssert(ast: NonNullAssert, context: any): any {
|
||||
ast.expression.visit(this);
|
||||
return null;
|
||||
}
|
||||
visitPropertyRead(ast: PropertyRead, context: any): any {
|
||||
ast.receiver.visit(this);
|
||||
return null;
|
||||
@ -379,6 +392,10 @@ export class AstTransformer implements AstVisitor {
|
||||
return new PrefixNot(ast.span, ast.expression.visit(this));
|
||||
}
|
||||
|
||||
visitNonNullAssert(ast: NonNullAssert, context: any): AST {
|
||||
return new NonNullAssert(ast.span, ast.expression.visit(this));
|
||||
}
|
||||
|
||||
visitConditional(ast: Conditional, context: any): AST {
|
||||
return new Conditional(
|
||||
ast.span, ast.condition.visit(this), ast.trueExp.visit(this), ast.falseExp.visit(this));
|
||||
@ -461,6 +478,7 @@ export function visitAstChildren(ast: AST, visitor: AstVisitor, context?: any) {
|
||||
visitAll(ast.args);
|
||||
},
|
||||
visitPrefixNot(ast) { visit(ast.expression); },
|
||||
visitNonNullAssert(ast) { visit(ast.expression); },
|
||||
visitPropertyRead(ast) { visit(ast.receiver); },
|
||||
visitPropertyWrite(ast) {
|
||||
visit(ast.receiver);
|
||||
|
@ -10,7 +10,8 @@ import * as chars from '../chars';
|
||||
import {CompilerInjectable} from '../injectable';
|
||||
import {DEFAULT_INTERPOLATION_CONFIG, InterpolationConfig} from '../ml_parser/interpolation_config';
|
||||
import {escapeRegExp} from '../util';
|
||||
import {AST, ASTWithSource, AstVisitor, Binary, BindingPipe, Chain, Conditional, EmptyExpr, FunctionCall, ImplicitReceiver, Interpolation, KeyedRead, KeyedWrite, LiteralArray, LiteralMap, LiteralPrimitive, MethodCall, ParseSpan, ParserError, PrefixNot, PropertyRead, PropertyWrite, Quote, SafeMethodCall, SafePropertyRead, TemplateBinding} from './ast';
|
||||
|
||||
import {AST, ASTWithSource, AstVisitor, Binary, BindingPipe, Chain, Conditional, EmptyExpr, FunctionCall, ImplicitReceiver, Interpolation, KeyedRead, KeyedWrite, LiteralArray, LiteralMap, LiteralPrimitive, MethodCall, NonNullAssert, ParseSpan, ParserError, PrefixNot, PropertyRead, PropertyWrite, Quote, SafeMethodCall, SafePropertyRead, TemplateBinding} from './ast';
|
||||
import {EOF, Lexer, Token, TokenType, isIdentifier, isQuote} from './lexer';
|
||||
|
||||
|
||||
@ -523,6 +524,9 @@ export class _ParseAST {
|
||||
this.expectCharacter(chars.$RPAREN);
|
||||
result = new FunctionCall(this.span(result.span.start), result, args);
|
||||
|
||||
} else if (this.optionalOperator('!')) {
|
||||
result = new NonNullAssert(this.span(result.span.start), result);
|
||||
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
@ -811,6 +815,8 @@ class SimpleExpressionChecker implements AstVisitor {
|
||||
|
||||
visitPrefixNot(ast: PrefixNot, context: any) {}
|
||||
|
||||
visitNonNullAssert(ast: NonNullAssert, context: any) {}
|
||||
|
||||
visitConditional(ast: Conditional, context: any) {}
|
||||
|
||||
visitPipe(ast: BindingPipe, context: any) { this.errors.push('pipes'); }
|
||||
|
Reference in New Issue
Block a user