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
This commit is contained in:
		
							parent
							
								
									fee28e20bb
								
							
						
					
					
						commit
						c1bb88603e
					
				| @ -129,7 +129,7 @@ export class NgForOf<T> implements DoCheck { | ||||
|    * [template input variable](guide/structural-directives#template-input-variable). | ||||
|    */ | ||||
|   @Input() | ||||
|   set ngForOf(ngForOf: NgIterable<T>) { | ||||
|   set ngForOf(ngForOf: NgIterable<T>|undefined|null) { | ||||
|     this._ngForOf = ngForOf; | ||||
|     this._ngForOfDirty = true; | ||||
|   } | ||||
| @ -165,8 +165,7 @@ export class NgForOf<T> implements DoCheck { | ||||
| 
 | ||||
|   get ngForTrackBy(): TrackByFunction<T> { return this._trackByFn; } | ||||
| 
 | ||||
|   // TODO(issue/24571): remove '!'.
 | ||||
|   private _ngForOf !: NgIterable<T>; | ||||
|   private _ngForOf: NgIterable<T>|undefined|null = null; | ||||
|   private _ngForOfDirty: boolean = true; | ||||
|   private _differ: IterableDiffer<T>|null = null; | ||||
|   // TODO(issue/24571): remove '!'.
 | ||||
| @ -219,8 +218,11 @@ export class NgForOf<T> implements DoCheck { | ||||
|         (item: IterableChangeRecord<any>, adjustedPreviousIndex: number | null, | ||||
|          currentIndex: number | null) => { | ||||
|           if (item.previousIndex == null) { | ||||
|             // NgForOf is never "null" or "undefined" here because the differ detected
 | ||||
|             // that a new item needs to be inserted from the iterable. This implies that
 | ||||
|             // there is an iterable value for "_ngForOf".
 | ||||
|             const view = this._viewContainer.createEmbeddedView( | ||||
|                 this._template, new NgForOfContext<T>(null !, this._ngForOf, -1, -1), | ||||
|                 this._template, new NgForOfContext<T>(null !, this._ngForOf !, -1, -1), | ||||
|                 currentIndex === null ? undefined : currentIndex); | ||||
|             const tuple = new RecordViewTuple<T>(item, view); | ||||
|             insertTuples.push(tuple); | ||||
| @ -243,7 +245,7 @@ export class NgForOf<T> implements DoCheck { | ||||
|       const viewRef = <EmbeddedViewRef<NgForOfContext<T>>>this._viewContainer.get(i); | ||||
|       viewRef.context.index = i; | ||||
|       viewRef.context.count = ilen; | ||||
|       viewRef.context.ngForOf = this._ngForOf; | ||||
|       viewRef.context.ngForOf = this._ngForOf !; | ||||
|     } | ||||
| 
 | ||||
|     changes.forEachIdentityChange((record: any) => { | ||||
|  | ||||
| @ -146,7 +146,7 @@ export class DefaultIterableDiffer<V> implements IterableDiffer<V>, IterableChan | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   diff(collection: NgIterable<V>): DefaultIterableDiffer<V>|null { | ||||
|   diff(collection: NgIterable<V>|null|undefined): DefaultIterableDiffer<V>|null { | ||||
|     if (collection == null) collection = []; | ||||
|     if (!isListLikeIterable(collection)) { | ||||
|       throw new Error( | ||||
|  | ||||
| @ -34,7 +34,7 @@ export interface IterableDiffer<V> { | ||||
|    * @returns an object describing the difference. The return value is only valid until the next | ||||
|    * `diff()` invocation. | ||||
|    */ | ||||
|   diff(object: NgIterable<V>): IterableChanges<V>|null; | ||||
|   diff(object: NgIterable<V>|undefined|null): IterableChanges<V>|null; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  | ||||
							
								
								
									
										2
									
								
								tools/public_api_guard/common/common.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								tools/public_api_guard/common/common.d.ts
									
									
									
									
										vendored
									
									
								
							| @ -239,7 +239,7 @@ export declare class NgComponentOutlet implements OnChanges, OnDestroy { | ||||
| } | ||||
| 
 | ||||
| export declare class NgForOf<T> implements DoCheck { | ||||
|     ngForOf: NgIterable<T>; | ||||
|     ngForOf: NgIterable<T> | undefined | null; | ||||
|     ngForTemplate: TemplateRef<NgForOfContext<T>>; | ||||
|     ngForTrackBy: TrackByFunction<T>; | ||||
|     constructor(_viewContainer: ViewContainerRef, _template: TemplateRef<NgForOfContext<T>>, _differs: IterableDiffers); | ||||
|  | ||||
							
								
								
									
										4
									
								
								tools/public_api_guard/core/core.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								tools/public_api_guard/core/core.d.ts
									
									
									
									
										vendored
									
									
								
							| @ -263,7 +263,7 @@ export declare class DefaultIterableDiffer<V> implements IterableDiffer<V>, Iter | ||||
|     readonly length: number; | ||||
|     constructor(trackByFn?: TrackByFunction<V>); | ||||
|     check(collection: NgIterable<V>): boolean; | ||||
|     diff(collection: NgIterable<V>): DefaultIterableDiffer<V> | null; | ||||
|     diff(collection: NgIterable<V> | null | undefined): DefaultIterableDiffer<V> | null; | ||||
|     forEachAddedItem(fn: (record: IterableChangeRecord_<V>) => void): void; | ||||
|     forEachIdentityChange(fn: (record: IterableChangeRecord_<V>) => void): void; | ||||
|     forEachItem(fn: (record: IterableChangeRecord_<V>) => void): void; | ||||
| @ -505,7 +505,7 @@ export interface IterableChanges<V> { | ||||
| } | ||||
| 
 | ||||
| export interface IterableDiffer<V> { | ||||
|     diff(object: NgIterable<V>): IterableChanges<V> | null; | ||||
|     diff(object: NgIterable<V> | undefined | null): IterableChanges<V> | null; | ||||
| } | ||||
| 
 | ||||
| export interface IterableDifferFactory { | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user