fix(core): make QueryList implement Iterable in the type system (#33536)
Originally, QueryList implemented Iterable and provided a Symbol.iterator on its prototype. This caused issues with tree-shaking, so QueryList was refactored and the Symbol.iterator added in its constructor instead. As part of this change, QueryList no longer implemented Iterable directly. Unfortunately, this meant that QueryList was no longer assignable to Iterable or, consequently, NgIterable. NgIterable is used for NgFor's input, so this meant that QueryList was not usable (in a type sense) for NgFor iteration. View Engine's template type checking would not catch this, but Ivy's did. As a fix, this commit adds the declaration (but not the implementation) of the Symbol.iterator function back to QueryList. This has no runtime effect, so it doesn't affect tree-shaking of QueryList, but it ensures that QueryList is assignable to NgIterable and thus usable with NgFor. Fixes #29842 PR Close #33536
This commit is contained in:
@ -85,3 +85,7 @@ export const NO_ERRORS_SCHEMA: any = false;
|
||||
export class EventEmitter<T> {
|
||||
subscribe(generatorOrNext?: any, error?: any, complete?: any): unknown { return null; }
|
||||
}
|
||||
|
||||
export interface QueryList<T>/* implements Iterable<T> */ { [Symbol.iterator]: () => Iterator<T>; }
|
||||
|
||||
export type NgIterable<T> = Array<T>| Iterable<T>;
|
||||
|
@ -28,7 +28,7 @@ import * as i0 from '@angular/core';
|
||||
|
||||
export declare class NgForOfContext<T> {
|
||||
$implicit: T;
|
||||
ngForOf: T[];
|
||||
ngForOf: i0.NgIterable<T>;
|
||||
index: number;
|
||||
count: number;
|
||||
readonly first: boolean;
|
||||
@ -44,7 +44,7 @@ export declare class IndexPipe {
|
||||
}
|
||||
|
||||
export declare class NgForOf<T> {
|
||||
ngForOf: T[];
|
||||
ngForOf: i0.NgIterable<T>;
|
||||
static ngTemplateContextGuard<T>(dir: NgForOf<T>, ctx: any): ctx is NgForOfContext<T>;
|
||||
static ɵdir: i0.ɵɵDirectiveDefWithMeta<NgForOf<any>, '[ngFor][ngForOf]', never, {'ngForOf': 'ngForOf'}, {}, never>;
|
||||
}
|
||||
@ -714,6 +714,30 @@ export declare class AnimationEvent {
|
||||
env.driveMain();
|
||||
});
|
||||
|
||||
it('should accept NgFor iteration over a QueryList', () => {
|
||||
env.tsconfig({fullTemplateTypeCheck: true, strictTemplates: true});
|
||||
env.write('test.ts', `
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {Component, NgModule, QueryList} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'test',
|
||||
template: '<div *ngFor="let user of users">{{user.name}}</div>',
|
||||
})
|
||||
class TestCmp {
|
||||
users!: QueryList<{name: string}>;
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
declarations: [TestCmp],
|
||||
imports: [CommonModule],
|
||||
})
|
||||
class Module {}
|
||||
`);
|
||||
|
||||
env.driveMain();
|
||||
});
|
||||
|
||||
it('should report an error with an unknown local ref target', () => {
|
||||
env.write('test.ts', `
|
||||
import {Component, NgModule} from '@angular/core';
|
||||
|
Reference in New Issue
Block a user