Skip to content

Commit c1bb886

Browse files
devversionalxhub
authored andcommitted
fix(common): expand type for "ngForOf" input to work with strict null checks (#31371)
Currently the `ngForOf` input accepts `null` or `undefined` as valid values. Although when using strict template input type checking (which will be supported by `ngtsc`), passing `null` or `undefined` with strict null checks enabled causes a type check failure because the type for the `ngForOf` input becomes too strict if strict null checks are enabled. The type of the input needs to be expanded to also accept `null` or `undefined` to behave consistently regardless of the `strictNullChecks` flag. This is necessary because whenever strict input type checking is enabled by default, most of the Angular projects that use `*ngFor` with the async pipe will either need to disable template type checking or strict null checks because the `async` pipe returns `null` if the observable hasn't been emitted yet. See for example how this affects the `angular/components` repository and how much bloat the workaround involves: https://github.com/angular/components/pull/16373/files#r296942696. PR Close #31371
1 parent fee28e2 commit c1bb886

File tree

5 files changed

+12
-10
lines changed

5 files changed

+12
-10
lines changed

packages/common/src/directives/ng_for_of.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ export class NgForOf<T> implements DoCheck {
129129
* [template input variable](guide/structural-directives#template-input-variable).
130130
*/
131131
@Input()
132-
set ngForOf(ngForOf: NgIterable<T>) {
132+
set ngForOf(ngForOf: NgIterable<T>|undefined|null) {
133133
this._ngForOf = ngForOf;
134134
this._ngForOfDirty = true;
135135
}
@@ -165,8 +165,7 @@ export class NgForOf<T> implements DoCheck {
165165

166166
get ngForTrackBy(): TrackByFunction<T> { return this._trackByFn; }
167167

168-
// TODO(issue/24571): remove '!'.
169-
private _ngForOf !: NgIterable<T>;
168+
private _ngForOf: NgIterable<T>|undefined|null = null;
170169
private _ngForOfDirty: boolean = true;
171170
private _differ: IterableDiffer<T>|null = null;
172171
// TODO(issue/24571): remove '!'.
@@ -219,8 +218,11 @@ export class NgForOf<T> implements DoCheck {
219218
(item: IterableChangeRecord<any>, adjustedPreviousIndex: number | null,
220219
currentIndex: number | null) => {
221220
if (item.previousIndex == null) {
221+
// NgForOf is never "null" or "undefined" here because the differ detected
222+
// that a new item needs to be inserted from the iterable. This implies that
223+
// there is an iterable value for "_ngForOf".
222224
const view = this._viewContainer.createEmbeddedView(
223-
this._template, new NgForOfContext<T>(null !, this._ngForOf, -1, -1),
225+
this._template, new NgForOfContext<T>(null !, this._ngForOf !, -1, -1),
224226
currentIndex === null ? undefined : currentIndex);
225227
const tuple = new RecordViewTuple<T>(item, view);
226228
insertTuples.push(tuple);
@@ -243,7 +245,7 @@ export class NgForOf<T> implements DoCheck {
243245
const viewRef = <EmbeddedViewRef<NgForOfContext<T>>>this._viewContainer.get(i);
244246
viewRef.context.index = i;
245247
viewRef.context.count = ilen;
246-
viewRef.context.ngForOf = this._ngForOf;
248+
viewRef.context.ngForOf = this._ngForOf !;
247249
}
248250

249251
changes.forEachIdentityChange((record: any) => {

packages/core/src/change_detection/differs/default_iterable_differ.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ export class DefaultIterableDiffer<V> implements IterableDiffer<V>, IterableChan
146146
}
147147
}
148148

149-
diff(collection: NgIterable<V>): DefaultIterableDiffer<V>|null {
149+
diff(collection: NgIterable<V>|null|undefined): DefaultIterableDiffer<V>|null {
150150
if (collection == null) collection = [];
151151
if (!isListLikeIterable(collection)) {
152152
throw new Error(

packages/core/src/change_detection/differs/iterable_differs.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export interface IterableDiffer<V> {
3434
* @returns an object describing the difference. The return value is only valid until the next
3535
* `diff()` invocation.
3636
*/
37-
diff(object: NgIterable<V>): IterableChanges<V>|null;
37+
diff(object: NgIterable<V>|undefined|null): IterableChanges<V>|null;
3838
}
3939

4040
/**

tools/public_api_guard/common/common.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ export declare class NgComponentOutlet implements OnChanges, OnDestroy {
239239
}
240240

241241
export declare class NgForOf<T> implements DoCheck {
242-
ngForOf: NgIterable<T>;
242+
ngForOf: NgIterable<T> | undefined | null;
243243
ngForTemplate: TemplateRef<NgForOfContext<T>>;
244244
ngForTrackBy: TrackByFunction<T>;
245245
constructor(_viewContainer: ViewContainerRef, _template: TemplateRef<NgForOfContext<T>>, _differs: IterableDiffers);

tools/public_api_guard/core/core.d.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ export declare class DefaultIterableDiffer<V> implements IterableDiffer<V>, Iter
263263
readonly length: number;
264264
constructor(trackByFn?: TrackByFunction<V>);
265265
check(collection: NgIterable<V>): boolean;
266-
diff(collection: NgIterable<V>): DefaultIterableDiffer<V> | null;
266+
diff(collection: NgIterable<V> | null | undefined): DefaultIterableDiffer<V> | null;
267267
forEachAddedItem(fn: (record: IterableChangeRecord_<V>) => void): void;
268268
forEachIdentityChange(fn: (record: IterableChangeRecord_<V>) => void): void;
269269
forEachItem(fn: (record: IterableChangeRecord_<V>) => void): void;
@@ -505,7 +505,7 @@ export interface IterableChanges<V> {
505505
}
506506

507507
export interface IterableDiffer<V> {
508-
diff(object: NgIterable<V>): IterableChanges<V> | null;
508+
diff(object: NgIterable<V> | undefined | null): IterableChanges<V> | null;
509509
}
510510

511511
export interface IterableDifferFactory {

0 commit comments

Comments
 (0)