feat(ivy): more accurate type narrowing for ngIf
directive (#30248)
A structural directive can specify a template guard for an input, such that the type of that input's binding can be narrowed based on the guard's return type. Previously, such template guards could only be methods, of which an invocation would be inserted into the type-check block (TCB). For `NgIf`, the template guard narrowed the type of its expression to be `NonNullable` using the following declaration: ```typescript export declare class NgIf { static ngTemplateGuard_ngIf<E>(dir: NgIf, expr: E): expr is NonNullable<E> } ``` This works fine for usages such as `*ngIf="person"` but starts to introduce false-positives when e.g. an explicit non-null check like `*ngIf="person !== null"` is used, as the method invocation in the TCB would not have the desired effect of narrowing `person` to become non-nullable: ```typescript if (NgIf.ngTemplateGuard_ngIf(directive, ctx.person !== null)) { // Usages of `ctx.person` within this block would // not have been narrowed to be non-nullable. } ``` This commit introduces a new strategy for template guards to allow for the binding expression itself to be used as template guard in the TCB. Now, the TCB generated for `*ngIf="person !== null"` would look as follows: ```typescript if (ctx.person !== null) { // This time `ctx.person` will successfully have // been narrowed to be non-nullable. } ``` This strategy can be activated by declaring the template guard as a property declaration with `'binding'` as literal return type. See #30235 for an example where this led to a false positive. PR Close #30248
This commit is contained in:
@ -219,12 +219,12 @@ export class NgIf {
|
||||
/**
|
||||
* Assert the correct type of the expression bound to the `ngIf` input within the template.
|
||||
*
|
||||
* The presence of this method is a signal to the Ivy template type check compiler that when the
|
||||
* `NgIf` structural directive renders its template, the type of the expression bound to `ngIf`
|
||||
* should be narrowed in some way. For `NgIf`, it is narrowed to be non-null, which allows the
|
||||
* strictNullChecks feature of TypeScript to work with `NgIf`.
|
||||
* The presence of this static field is a signal to the Ivy template type check compiler that
|
||||
* when the `NgIf` structural directive renders its template, the type of the expression bound
|
||||
* to `ngIf` should be narrowed in some way. For `NgIf`, the binding expression itself is used to
|
||||
* narrow its type, which allows the strictNullChecks feature of TypeScript to work with `NgIf`.
|
||||
*/
|
||||
static ngTemplateGuard_ngIf<E>(dir: NgIf, expr: E): expr is NonNullable<E> { return true; }
|
||||
static ngTemplateGuard_ngIf: 'binding';
|
||||
}
|
||||
|
||||
/**
|
||||
|
Reference in New Issue
Block a user