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 e54b2e762bc..84798356043 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 @@ -2348,7 +2348,7 @@ describe('type check blocks', () => { `; const result = tcb(TEMPLATE); - expect(result).toContain('for (const _t1 of (((this).items))!) {'); + expect(result).toContain('for (const _t1 of ((this).items)!) {'); expect(result).toContain('"" + ((this).main(_t1))'); expect(result).toContain('"" + ((this).empty())'); }); @@ -2361,7 +2361,7 @@ describe('type check blocks', () => { `; const result = tcb(TEMPLATE); - expect(result).toContain('for (const _t1 of (((this).items))!) {'); + expect(result).toContain('for (const _t1 of ((this).items)!) {'); expect(result).toContain('var _t2 = null! as number;'); expect(result).toContain('var _t3 = null! as boolean;'); expect(result).toContain('var _t4 = null! as boolean;'); @@ -2379,7 +2379,7 @@ describe('type check blocks', () => { `; const result = tcb(TEMPLATE); - expect(result).toContain('for (const _t1 of (((this).items))!) {'); + expect(result).toContain('for (const _t1 of ((this).items)!) {'); expect(result).toContain('var _t2 = null! as number;'); expect(result).toContain('var _t3 = null! as boolean;'); expect(result).toContain('var _t4 = null! as boolean;'); @@ -2395,7 +2395,7 @@ describe('type check blocks', () => { `; const result = tcb(TEMPLATE); - expect(result).toContain('for (const _t1 of (((this).items))!) {'); + expect(result).toContain('for (const _t1 of ((this).items)!) {'); expect(result).toContain('var _t2 = null! as number;'); expect(result).toContain('var _t3 = null! as number;'); expect(result).toContain('"" + (_t2) + (_t3)'); @@ -2413,15 +2413,15 @@ describe('type check blocks', () => { `; const result = tcb(TEMPLATE); - expect(result).toContain('for (const _t1 of (((this).items))!) { var _t2 = null! as number;'); + expect(result).toContain('for (const _t1 of ((this).items)!) { var _t2 = null! as number;'); expect(result).toContain('"" + (_t1) + (_t2)'); - expect(result).toContain('for (const _t3 of (((_t1).items))!) { var _t4 = null! as number;'); + expect(result).toContain('for (const _t3 of ((_t1).items)!) { var _t4 = null! as number;'); expect(result).toContain('"" + (_t1) + ((_t2)) + (_t3) + (_t4)'); }); it('should generate the tracking expression of a for loop', () => { const result = tcb(`@for (item of items; track trackingFn($index, item, prop)) {}`); - expect(result).toContain('for (const _t1 of (((this).items))!) { var _t2 = null! as number;'); + expect(result).toContain('for (const _t1 of ((this).items)!) { var _t2 = null! as number;'); expect(result).toContain('(this).trackingFn(_t2, _t1, ((this).prop));'); }); @@ -2435,23 +2435,10 @@ describe('type check blocks', () => { `; const result = tcb(TEMPLATE, undefined, {checkControlFlowBodies: false}); - expect(result).toContain('for (const _t1 of (((this).items))!) {'); + expect(result).toContain('for (const _t1 of ((this).items)!) {'); expect(result).not.toContain('.main'); expect(result).not.toContain('.empty'); }); - - it('should wrap compound expressions in parentheses before appending non-null assertion', () => { - const TEMPLATE = ` - @for (item of items && items.slice(0, 10); track item) { - {{item}} - } - `; - - const result = tcb(TEMPLATE); - expect(result).toContain( - 'for (const _t1 of ((((this).items)) && ((((this).items)).slice(0, 10)))!) {', - ); - }); }); describe('let declarations', () => { diff --git a/packages/compiler-cli/test/ngtsc/template_typecheck_spec.ts b/packages/compiler-cli/test/ngtsc/template_typecheck_spec.ts index 850013a575c..f693bb3d853 100644 --- a/packages/compiler-cli/test/ngtsc/template_typecheck_spec.ts +++ b/packages/compiler-cli/test/ngtsc/template_typecheck_spec.ts @@ -7120,134 +7120,6 @@ suppress ]); }); - it('should report diagnostics within sub-expressions of compound @for loop expressions', () => { - env.write( - 'test.ts', - ` - import {Component} from '@angular/core'; - - @Component({ - template: \` - @for (item of items && does_not_exist; track item) { - {{item}} - } - \`, - }) - export class Main { - items = [1, 2, 3]; - } - `, - ); - - const diags = env.driveDiagnostics(); - expect(diags.map((d) => ts.flattenDiagnosticMessageText(d.messageText, ''))).toEqual([ - "Property 'does_not_exist' does not exist on type 'Main'.", - ]); - }); - - it('should report diagnostics on invalid arguments in compound @for loop expressions', () => { - env.write( - 'test.ts', - ` - import {Component} from '@angular/core'; - - @Component({ - template: \` - @for (item of items && items.slice('not_a_number'); track item) { - {{item}} - } - \`, - }) - export class Main { - items = [1, 2, 3]; - } - `, - ); - - const diags = env.driveDiagnostics(); - expect(diags.map((d) => ts.flattenDiagnosticMessageText(d.messageText, ''))).toEqual([ - "Argument of type 'string' is not assignable to parameter of type 'number'.", - ]); - }); - - it('should report diagnostics on ternary expressions in @for loop expressions', () => { - env.write( - 'test.ts', - ` - import {Component} from '@angular/core'; - - @Component({ - template: \` - @for (item of condition ? not_found : items; track item) { - {{item}} - } - \`, - }) - export class Main { - condition = true; - items = [1, 2, 3]; - } - `, - ); - - const diags = env.driveDiagnostics(); - expect(diags.map((d) => ts.flattenDiagnosticMessageText(d.messageText, ''))).toEqual([ - "Property 'not_found' does not exist on type 'Main'.", - ]); - }); - - it('should report diagnostics on nested property reads in @for loop expressions', () => { - env.write( - 'test.ts', - ` - import {Component} from '@angular/core'; - - @Component({ - template: \` - @for (item of nested.does_not_exist; track item) { - {{item}} - } - \`, - }) - export class Main { - nested = {a: 1}; - } - `, - ); - - const diags = env.driveDiagnostics(); - expect(diags.map((d) => ts.flattenDiagnosticMessageText(d.messageText, ''))).toEqual([ - "Property 'does_not_exist' does not exist on type '{ a: number; }'.", - ]); - }); - - it('should report diagnostics when calling functions with invalid arguments in @for loop expressions', () => { - env.write( - 'test.ts', - ` - import {Component} from '@angular/core'; - - @Component({ - template: \` - @for (item of getItems('invalid'); track item) { - {{item}} - } - \`, - }) - export class Main { - getItems(count: number): string[] { - return []; - } - } - `, - ); - - const diags = env.driveDiagnostics(); - expect(diags.map((d) => ts.flattenDiagnosticMessageText(d.messageText, ''))).toEqual([ - "Argument of type 'string' is not assignable to parameter of type 'number'.", - ]); - }); - it('should check for loop variables with the same name as built-in globals', () => { // strictTemplates are necessary so the event listener is checked. env.tsconfig({strictTemplates: true}); diff --git a/packages/compiler/src/typecheck/ops/for_block.ts b/packages/compiler/src/typecheck/ops/for_block.ts index 85af71caaca..b5850f103c9 100644 --- a/packages/compiler/src/typecheck/ops/for_block.ts +++ b/packages/compiler/src/typecheck/ops/for_block.ts @@ -45,9 +45,8 @@ export class TcbForOfOp extends TcbOp { // It's common to have a for loop over a nullable value (e.g. produced by the `async` pipe). // Add a non-null expression to allow such values to be assigned. - const expr = tcbExpression(this.block.expression, this.tcb, this.scope).wrapForTypeChecker(); - const expression = new TcbExpr(`${expr.print()}!`).addParseSpanInfo( - this.block.expression.sourceSpan, + const expression = new TcbExpr( + `${tcbExpression(this.block.expression, this.tcb, this.scope).print()}!`, ); let statements: TcbExpr[];