fix(compiler-cli): allow '==' to compare nullable types (#16731)

Fixes: #16729

* fix(compiler-cli): diagnose issues in conditional of ternary

Fixes: #16730
This commit is contained in:
Chuck Jazdzewski 2017-05-16 16:36:51 -07:00 committed by Jason Aden
parent c805082648
commit d761059e4d
2 changed files with 37 additions and 3 deletions

View File

@ -51,8 +51,29 @@ export class AstType implements AstVisitor {
return kind; return kind;
} }
const leftType = this.getType(ast.left); const getType = (ast: AST, operation: string): Symbol => {
const rightType = this.getType(ast.right); const type = this.getType(ast);
if (type.nullable) {
switch (operation) {
case '&&':
case '||':
case '==':
case '!=':
case '===':
case '!==':
// Nullable allowed.
break;
default:
this.reportError(`The expression might be null`, ast);
break;
}
return this.query.getNonNullableType(type);
}
return type;
};
const leftType = getType(ast.left, ast.operation);
const rightType = getType(ast.right, ast.operation);
const leftRawKind = this.query.getTypeKind(leftType); const leftRawKind = this.query.getTypeKind(leftType);
const rightRawKind = this.query.getTypeKind(rightType); const rightRawKind = this.query.getTypeKind(rightType);
const leftKind = normalize(leftRawKind, rightRawKind); const leftKind = normalize(leftRawKind, rightRawKind);
@ -166,6 +187,9 @@ export class AstType implements AstVisitor {
visitConditional(ast: Conditional) { visitConditional(ast: Conditional) {
// The type of a conditional is the union of the true and false conditions. // The type of a conditional is the union of the true and false conditions.
if (this.diagnostics) {
visitAstChildren(ast, this);
}
return this.query.getTypeUnion(this.getType(ast.trueExp), this.getType(ast.falseExp)); return this.query.getTypeUnion(this.getType(ast.trueExp), this.getType(ast.falseExp));
} }

View File

@ -175,7 +175,16 @@ describe('expression diagnostics', () => {
it('should reject an uncalled event handler', it('should reject an uncalled event handler',
() => reject( () => reject(
'<div (click)="click">{{person.name.first}}</div>', 'Unexpected callable expression')); '<div (click)="click">{{person.name.first}}</div>', 'Unexpected callable expression'));
describe('with comparisions between nullable and non-nullable', () => {
it('should accept ==', () => accept(`<div>{{e == 1 ? 'a' : 'b'}}</div>`));
it('should accept ===', () => accept(`<div>{{e === 1 ? 'a' : 'b'}}</div>`));
it('should accept !=', () => accept(`<div>{{e != 1 ? 'a' : 'b'}}</div>`));
it('should accept !==', () => accept(`<div>{{e !== 1 ? 'a' : 'b'}}</div>`));
it('should accept &&', () => accept(`<div>{{e && 1 ? 'a' : 'b'}}</div>`));
it('should accept ||', () => accept(`<div>{{e || 1 ? 'a' : 'b'}}</div>`));
it('should reject >',
() => reject(`<div>{{e > 1 ? 'a' : 'b'}}</div>`, 'The expression might be null'));
});
}); });
const FILES: Directory = { const FILES: Directory = {
@ -216,6 +225,7 @@ const FILES: Directory = {
promised_people: Promise<Person[]>; promised_people: Promise<Person[]>;
private private_person: Person; private private_person: Person;
private private_people: Person[]; private private_people: Person[];
e?: number;
getPerson(): Person { return this.person; } getPerson(): Person { return this.person; }
click() {} click() {}