mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
Revert "fix(compiler): wrap @for collection expression before appending non-null assertion"
This reverts commited8f16c078. This caused a breakage in TGP because the changes (correctly) produced new diagnostics on ` @for (a of (x | async) || []; track a) {` x is `any`. This results in `<any> | async` which produces `unknown | null` and then the `@if` finally narrows this down to `{}` which isn't iteratable. (cherry picked from commit37ca679192)
This commit is contained in:
@@ -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', () => {
|
||||
|
||||
@@ -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});
|
||||
|
||||
@@ -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[];
|
||||
|
||||
Reference in New Issue
Block a user