Skip to content

Commit dc3f7cb

Browse files
crisbetodylhunn
authored andcommitted
fix(compiler): template type checking not reporting diagnostics for incompatible type comparisons (#52322)
In #52110 the compiler was changed to produce `if` statements when type checking `@switch` in order to avoid a bug in the TypeScript compiler. In order to avoid duplicate diagnostics, the main `@switch` expression was ignored in each of the `@case` comparisons. This appears to have caused a regression where comparing incompatible types wasn't being reported anymore. These changes resolve the issue by wrapping the expression in parentheses which allows the compiler to report comparison diagnostics while ignoring diagnostics in the expression itself. Fixes #52315. PR Close #52322
1 parent 3278966 commit dc3f7cb

3 files changed

Lines changed: 39 additions & 10 deletions

File tree

packages/compiler-cli/src/ngtsc/typecheck/src/type_check_block.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1327,12 +1327,15 @@ class TcbSwitchOp extends TcbOp {
13271327
}
13281328

13291329
override execute(): null {
1330-
const expression = tcbExpression(this.block.expression, this.tcb, this.scope);
1331-
13321330
// Since we'll use the expression in multiple comparisons, we don't want to
13331331
// log the same diagnostic more than once. Ignore this expression since we already
13341332
// produced a `TcbExpressionOp`.
1335-
markIgnoreDiagnostics(expression);
1333+
const comparisonExpression = tcbExpression(this.block.expression, this.tcb, this.scope);
1334+
markIgnoreDiagnostics(comparisonExpression);
1335+
1336+
// Wrap the comparisson expression in parentheses so we don't ignore
1337+
// diagnostics when comparing incompatible types (see #52315).
1338+
const expression = ts.factory.createParenthesizedExpression(comparisonExpression);
13361339
const root = this.generateCase(0, expression, null);
13371340

13381341
if (root !== undefined) {

packages/compiler-cli/src/ngtsc/typecheck/test/type_check_block_spec.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1468,8 +1468,8 @@ describe('type check blocks', () => {
14681468

14691469
expect(tcb(TEMPLATE))
14701470
.toContain(
1471-
'if (((this).expr) === 1) { "" + ((this).one()); } else if ' +
1472-
'(((this).expr) === 2) { "" + ((this).two()); } else { "" + ((this).default()); }');
1471+
'if ((((this).expr)) === 1) { "" + ((this).one()); } else if ' +
1472+
'((((this).expr)) === 2) { "" + ((this).two()); } else { "" + ((this).default()); }');
14731473
});
14741474

14751475
it('should generate a switch block that only has a default case', () => {
@@ -1501,9 +1501,10 @@ describe('type check blocks', () => {
15011501

15021502
const result = tcb(TEMPLATE);
15031503

1504-
expect(result).toContain(`if (((this).expr) === 1) (this).one();`);
1505-
expect(result).toContain(`if (((this).expr) === 2) (this).two();`);
1506-
expect(result).toContain(`if (((this).expr) !== 1 && ((this).expr) !== 2) (this).default();`);
1504+
expect(result).toContain(`if ((((this).expr)) === 1) (this).one();`);
1505+
expect(result).toContain(`if ((((this).expr)) === 2) (this).two();`);
1506+
expect(result).toContain(
1507+
`if ((((this).expr)) !== 1 && (((this).expr)) !== 2) (this).default();`);
15071508
});
15081509

15091510
it('should generate a switch block inside a template', () => {
@@ -1526,8 +1527,8 @@ describe('type check blocks', () => {
15261527
expect(tcb(TEMPLATE))
15271528
.toContain(
15281529
'var _t1: any = null!; { var _t2 = (_t1.exp); _t2(); ' +
1529-
'if (_t2() === "one") { "" + ((this).one()); } ' +
1530-
'else if (_t2() === "two") { "" + ((this).two()); } ' +
1530+
'if ((_t2()) === "one") { "" + ((this).one()); } ' +
1531+
'else if ((_t2()) === "two") { "" + ((this).two()); } ' +
15311532
'else { "" + ((this).default()); } }');
15321533
});
15331534
});

packages/compiler-cli/test/ngtsc/template_typecheck_spec.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4294,6 +4294,31 @@ suppress
42944294
]);
42954295
});
42964296

4297+
it('should produce a diagnostic if @switch and @case have different types', () => {
4298+
env.write('test.ts', `
4299+
import {Component} from '@angular/core';
4300+
4301+
@Component({
4302+
template: \`
4303+
@switch (expr) {
4304+
@case (1) {
4305+
{{expr}}
4306+
}
4307+
}
4308+
\`,
4309+
standalone: true,
4310+
})
4311+
export class Main {
4312+
expr = true;
4313+
}
4314+
`);
4315+
4316+
const diags = env.driveDiagnostics();
4317+
expect(diags.map(d => ts.flattenDiagnosticMessageText(d.messageText, ''))).toEqual([
4318+
`This comparison appears to be unintentional because the types 'boolean' and 'number' have no overlap.`,
4319+
]);
4320+
});
4321+
42974322
it('should narrow the type in listener inside switch cases with expressions', () => {
42984323
env.write('test.ts', `
42994324
import {Component} from '@angular/core';

0 commit comments

Comments
 (0)