fix(common): reflect input type in NgIf context (#33997)
Fixes the content of `NgIf` being typed to any. Fixes #31556. PR Close #33997
This commit is contained in:
parent
a6b6d74c00
commit
02958c07f6
|
@ -149,14 +149,14 @@ import {Directive, EmbeddedViewRef, Input, TemplateRef, ViewContainerRef, ɵstri
|
|||
* @publicApi
|
||||
*/
|
||||
@Directive({selector: '[ngIf]'})
|
||||
export class NgIf {
|
||||
private _context: NgIfContext = new NgIfContext();
|
||||
private _thenTemplateRef: TemplateRef<NgIfContext>|null = null;
|
||||
private _elseTemplateRef: TemplateRef<NgIfContext>|null = null;
|
||||
private _thenViewRef: EmbeddedViewRef<NgIfContext>|null = null;
|
||||
private _elseViewRef: EmbeddedViewRef<NgIfContext>|null = null;
|
||||
export class NgIf<T> {
|
||||
private _context: NgIfContext<T> = new NgIfContext<T>();
|
||||
private _thenTemplateRef: TemplateRef<NgIfContext<T>>|null = null;
|
||||
private _elseTemplateRef: TemplateRef<NgIfContext<T>>|null = null;
|
||||
private _thenViewRef: EmbeddedViewRef<NgIfContext<T>>|null = null;
|
||||
private _elseViewRef: EmbeddedViewRef<NgIfContext<T>>|null = null;
|
||||
|
||||
constructor(private _viewContainer: ViewContainerRef, templateRef: TemplateRef<NgIfContext>) {
|
||||
constructor(private _viewContainer: ViewContainerRef, templateRef: TemplateRef<NgIfContext<T>>) {
|
||||
this._thenTemplateRef = templateRef;
|
||||
}
|
||||
|
||||
|
@ -164,7 +164,7 @@ export class NgIf {
|
|||
* The Boolean expression to evaluate as the condition for showing a template.
|
||||
*/
|
||||
@Input()
|
||||
set ngIf(condition: any) {
|
||||
set ngIf(condition: T) {
|
||||
this._context.$implicit = this._context.ngIf = condition;
|
||||
this._updateView();
|
||||
}
|
||||
|
@ -173,7 +173,7 @@ export class NgIf {
|
|||
* A template to show if the condition expression evaluates to true.
|
||||
*/
|
||||
@Input()
|
||||
set ngIfThen(templateRef: TemplateRef<NgIfContext>|null) {
|
||||
set ngIfThen(templateRef: TemplateRef<NgIfContext<T>>|null) {
|
||||
assertTemplate('ngIfThen', templateRef);
|
||||
this._thenTemplateRef = templateRef;
|
||||
this._thenViewRef = null; // clear previous view if any.
|
||||
|
@ -184,7 +184,7 @@ export class NgIf {
|
|||
* A template to show if the condition expression evaluates to false.
|
||||
*/
|
||||
@Input()
|
||||
set ngIfElse(templateRef: TemplateRef<NgIfContext>|null) {
|
||||
set ngIfElse(templateRef: TemplateRef<NgIfContext<T>>|null) {
|
||||
assertTemplate('ngIfElse', templateRef);
|
||||
this._elseTemplateRef = templateRef;
|
||||
this._elseViewRef = null; // clear previous view if any.
|
||||
|
@ -225,14 +225,22 @@ export class NgIf {
|
|||
* narrow its type, which allows the strictNullChecks feature of TypeScript to work with `NgIf`.
|
||||
*/
|
||||
static ngTemplateGuard_ngIf: 'binding';
|
||||
|
||||
/**
|
||||
* Asserts the correct type of the context for the template that `NgIf` will render.
|
||||
*
|
||||
* The presence of this method is a signal to the Ivy template type-check compiler that the
|
||||
* `NgIf` structural directive renders its template with a specific context type.
|
||||
*/
|
||||
static ngTemplateContextGuard<T>(dir: NgIf<T>, ctx: any): ctx is NgIfContext<T> { return true; }
|
||||
}
|
||||
|
||||
/**
|
||||
* @publicApi
|
||||
*/
|
||||
export class NgIfContext {
|
||||
public $implicit: any = null;
|
||||
public ngIf: any = null;
|
||||
export class NgIfContext<T> {
|
||||
public $implicit: T = null !;
|
||||
public ngIf: T = null !;
|
||||
}
|
||||
|
||||
function assertTemplate(property: string, templateRef: TemplateRef<any>| null): void {
|
||||
|
|
|
@ -64,10 +64,19 @@ export declare class NgForOf<T, U extends i0.NgIterable<T>> implements DoCheck {
|
|||
static ɵdir: i0.ɵɵDirectiveDefWithMeta<NgForOf<any>, '[ngFor][ngForOf]', never, {'ngForOf': 'ngForOf'}, {}, never>;
|
||||
}
|
||||
|
||||
export declare class NgIf {
|
||||
ngIf: any;
|
||||
export declare class NgIf<T> {
|
||||
ngIf: T;
|
||||
ngIfElse: TemplateRef<NgIfContext<T>> | null;
|
||||
ngIfThen: TemplateRef<NgIfContext<T>> | null;
|
||||
constructor(_viewContainer: ViewContainerRef, templateRef: TemplateRef<NgIfContext<T>>);
|
||||
static ngTemplateGuard_ngIf: 'binding';
|
||||
static ɵdir: i0.ɵɵDirectiveDefWithMeta<NgForOf<any>, '[ngIf]', never, {'ngIf': 'ngIf'}, {}, never>;
|
||||
static ngTemplateContextGuard<T>(dir: NgIf<T>, ctx: any): ctx is NgIfContext<T>;
|
||||
static ɵdir: i0.ɵɵDirectiveDefWithMeta<NgIf<any>, '[ngIf]', never, {'ngIf': 'ngIf'}, {}, never>;
|
||||
}
|
||||
|
||||
export declare class NgIfContext<T> {
|
||||
$implicit: T;
|
||||
ngIf: T;
|
||||
}
|
||||
|
||||
export declare class CommonModule {
|
||||
|
@ -815,6 +824,33 @@ export declare class AnimationEvent {
|
|||
expect(diags.length).toBe(0);
|
||||
});
|
||||
|
||||
it('should infer the context of NgIf', () => {
|
||||
env.tsconfig({fullTemplateTypeCheck: true, strictTemplates: true});
|
||||
env.write('test.ts', `
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {Component, NgModule} from '@angular/core';
|
||||
@Component({
|
||||
selector: 'test',
|
||||
template: '<div *ngIf="getUser(); let user">{{user.nonExistingProp}}</div>',
|
||||
})
|
||||
class TestCmp {
|
||||
getUser(): {name: string} {
|
||||
return {name: 'frodo'};
|
||||
}
|
||||
}
|
||||
@NgModule({
|
||||
declarations: [TestCmp],
|
||||
imports: [CommonModule],
|
||||
})
|
||||
class Module {}
|
||||
`);
|
||||
|
||||
const diags = env.driveDiagnostics();
|
||||
expect(diags.length).toBe(1);
|
||||
expect(diags[0].messageText)
|
||||
.toBe(`Property 'nonExistingProp' does not exist on type '{ name: string; }'.`);
|
||||
});
|
||||
|
||||
it('should report an error with an unknown local ref target', () => {
|
||||
env.write('test.ts', `
|
||||
import {Component, NgModule} from '@angular/core';
|
||||
|
|
|
@ -12,7 +12,7 @@ import {IterableDiffers, NgIterable, TemplateRef, ViewContainerRef} from '@angul
|
|||
import {DirectiveType, ɵɵNgOnChangesFeature, ɵɵdefineDirective, ɵɵdirectiveInject} from '../../src/render3/index';
|
||||
|
||||
export const NgForOf: DirectiveType<NgForOfDef<any, NgIterable<any>>> = NgForOfDef as any;
|
||||
export const NgIf: DirectiveType<NgIfDef> = NgIfDef as any;
|
||||
export const NgIf: DirectiveType<NgIfDef<any>> = NgIfDef as any;
|
||||
export const NgTemplateOutlet: DirectiveType<NgTemplateOutletDef> = NgTemplateOutletDef as any;
|
||||
|
||||
NgForOf.ɵdir = ɵɵdefineDirective({
|
||||
|
|
|
@ -235,17 +235,18 @@ export declare class NgForOfContext<T, U extends NgIterable<T>> {
|
|||
constructor($implicit: T, ngForOf: U, index: number, count: number);
|
||||
}
|
||||
|
||||
export declare class NgIf {
|
||||
ngIf: any;
|
||||
ngIfElse: TemplateRef<NgIfContext> | null;
|
||||
ngIfThen: TemplateRef<NgIfContext> | null;
|
||||
constructor(_viewContainer: ViewContainerRef, templateRef: TemplateRef<NgIfContext>);
|
||||
export declare class NgIf<T> {
|
||||
ngIf: T;
|
||||
ngIfElse: TemplateRef<NgIfContext<T>> | null;
|
||||
ngIfThen: TemplateRef<NgIfContext<T>> | null;
|
||||
constructor(_viewContainer: ViewContainerRef, templateRef: TemplateRef<NgIfContext<T>>);
|
||||
static ngTemplateGuard_ngIf: 'binding';
|
||||
static ngTemplateContextGuard<T>(dir: NgIf<T>, ctx: any): ctx is NgIfContext<T>;
|
||||
}
|
||||
|
||||
export declare class NgIfContext {
|
||||
$implicit: any;
|
||||
ngIf: any;
|
||||
export declare class NgIfContext<T> {
|
||||
$implicit: T;
|
||||
ngIf: T;
|
||||
}
|
||||
|
||||
export declare class NgLocaleLocalization extends NgLocalization {
|
||||
|
|
Loading…
Reference in New Issue