Skip to content

Commit 06f0855

Browse files
ranma42Splaktar
authored andcommitted
fix(common): narrow NgIf context variables in template type checker (angular#36627)
When the `NgIf` directive is used in a template, its context variables can be used to capture the bound value. This is sometimes used in complex expressions, where the resulting value is captured in a context variable. There's two syntax forms available: 1. Binding to `NgIfContext.ngIf` using the `as` syntax: ```html <span *ngIf="enabled && user as u">{{u.name}}</span> ``` 2. Binding to `NgIfContext.$implicit` using the `let` syntax: ```html <span *ngIf="enabled && user; let u">{{u.name}}</span> ``` Because of the semantics of `ngIf`, it is known that the captured context variable is truthy, however the template type checker would not consider them as such and still report errors when `strict` is enabled. This commit updates `NgIf`'s context guard to make the types of the context variables truthy, avoiding the issue. Based on angular#35125 PR Close angular#36627
1 parent 38fb735 commit 06f0855

File tree

3 files changed

+6
-5
lines changed

3 files changed

+6
-5
lines changed

goldens/public-api/common/common.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ export declare class NgIf<T = unknown> {
243243
set ngIfThen(templateRef: TemplateRef<NgIfContext<T>> | null);
244244
constructor(_viewContainer: ViewContainerRef, templateRef: TemplateRef<NgIfContext<T>>);
245245
static ngTemplateGuard_ngIf: 'binding';
246-
static ngTemplateContextGuard<T>(dir: NgIf<T>, ctx: any): ctx is NgIfContext<NonNullable<T>>;
246+
static ngTemplateContextGuard<T>(dir: NgIf<T>, ctx: any): ctx is NgIfContext<Exclude<T, false | 0 | '' | null | undefined>>;
247247
}
248248

249249
export declare class NgIfContext<T = unknown> {

packages/common/src/directives/ng_if.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,8 @@ export class NgIf<T = unknown> {
232232
* The presence of this method is a signal to the Ivy template type-check compiler that the
233233
* `NgIf` structural directive renders its template with a specific context type.
234234
*/
235-
static ngTemplateContextGuard<T>(dir: NgIf<T>, ctx: any): ctx is NgIfContext<NonNullable<T>> {
235+
static ngTemplateContextGuard<T>(dir: NgIf<T>, ctx: any):
236+
ctx is NgIfContext<Exclude<T, false|0|''|null|undefined>> {
236237
return true;
237238
}
238239
}

packages/compiler-cli/test/ngtsc/template_typecheck_spec.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ export declare class NgIf<T = unknown> {
7272
ngIfThen: TemplateRef<NgIfContext<T>> | null;
7373
constructor(_viewContainer: ViewContainerRef, templateRef: TemplateRef<NgIfContext<T>>);
7474
static ngTemplateGuard_ngIf: 'binding';
75-
static ngTemplateContextGuard<T>(dir: NgIf<T>, ctx: any): ctx is NgIfContext<NonNullable<T>>;
75+
static ngTemplateContextGuard<T>(dir: NgIf<T>, ctx: any): ctx is NgIfContext<Exclude<T, false | 0 | "" | null | undefined>>;
7676
static ɵdir: i0.ɵɵDirectiveDefWithMeta<NgIf<any>, '[ngIf]', never, {'ngIf': 'ngIf'}, {}, never>;
7777
}
7878
@@ -818,7 +818,7 @@ export declare class AnimationEvent {
818818
template: '<div *ngIf="user; let u">{{u.name}}</div>',
819819
})
820820
class TestCmp {
821-
user: {name: string}|null;
821+
user: {name: string}|null|false;
822822
}
823823
824824
@NgModule({
@@ -842,7 +842,7 @@ export declare class AnimationEvent {
842842
template: '<div *ngIf="user as u">{{u.name}}</div>',
843843
})
844844
class TestCmp {
845-
user: {name: string}|null;
845+
user: {name: string}|null|false;
846846
}
847847
848848
@NgModule({

0 commit comments

Comments
 (0)