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
|
* @publicApi
|
||||||
*/
|
*/
|
||||||
@Directive({selector: '[ngIf]'})
|
@Directive({selector: '[ngIf]'})
|
||||||
export class NgIf {
|
export class NgIf<T> {
|
||||||
private _context: NgIfContext = new NgIfContext();
|
private _context: NgIfContext<T> = new NgIfContext<T>();
|
||||||
private _thenTemplateRef: TemplateRef<NgIfContext>|null = null;
|
private _thenTemplateRef: TemplateRef<NgIfContext<T>>|null = null;
|
||||||
private _elseTemplateRef: TemplateRef<NgIfContext>|null = null;
|
private _elseTemplateRef: TemplateRef<NgIfContext<T>>|null = null;
|
||||||
private _thenViewRef: EmbeddedViewRef<NgIfContext>|null = null;
|
private _thenViewRef: EmbeddedViewRef<NgIfContext<T>>|null = null;
|
||||||
private _elseViewRef: EmbeddedViewRef<NgIfContext>|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;
|
this._thenTemplateRef = templateRef;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,7 +164,7 @@ export class NgIf {
|
||||||
* The Boolean expression to evaluate as the condition for showing a template.
|
* The Boolean expression to evaluate as the condition for showing a template.
|
||||||
*/
|
*/
|
||||||
@Input()
|
@Input()
|
||||||
set ngIf(condition: any) {
|
set ngIf(condition: T) {
|
||||||
this._context.$implicit = this._context.ngIf = condition;
|
this._context.$implicit = this._context.ngIf = condition;
|
||||||
this._updateView();
|
this._updateView();
|
||||||
}
|
}
|
||||||
|
@ -173,7 +173,7 @@ export class NgIf {
|
||||||
* A template to show if the condition expression evaluates to true.
|
* A template to show if the condition expression evaluates to true.
|
||||||
*/
|
*/
|
||||||
@Input()
|
@Input()
|
||||||
set ngIfThen(templateRef: TemplateRef<NgIfContext>|null) {
|
set ngIfThen(templateRef: TemplateRef<NgIfContext<T>>|null) {
|
||||||
assertTemplate('ngIfThen', templateRef);
|
assertTemplate('ngIfThen', templateRef);
|
||||||
this._thenTemplateRef = templateRef;
|
this._thenTemplateRef = templateRef;
|
||||||
this._thenViewRef = null; // clear previous view if any.
|
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.
|
* A template to show if the condition expression evaluates to false.
|
||||||
*/
|
*/
|
||||||
@Input()
|
@Input()
|
||||||
set ngIfElse(templateRef: TemplateRef<NgIfContext>|null) {
|
set ngIfElse(templateRef: TemplateRef<NgIfContext<T>>|null) {
|
||||||
assertTemplate('ngIfElse', templateRef);
|
assertTemplate('ngIfElse', templateRef);
|
||||||
this._elseTemplateRef = templateRef;
|
this._elseTemplateRef = templateRef;
|
||||||
this._elseViewRef = null; // clear previous view if any.
|
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`.
|
* narrow its type, which allows the strictNullChecks feature of TypeScript to work with `NgIf`.
|
||||||
*/
|
*/
|
||||||
static ngTemplateGuard_ngIf: 'binding';
|
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
|
* @publicApi
|
||||||
*/
|
*/
|
||||||
export class NgIfContext {
|
export class NgIfContext<T> {
|
||||||
public $implicit: any = null;
|
public $implicit: T = null !;
|
||||||
public ngIf: any = null;
|
public ngIf: T = null !;
|
||||||
}
|
}
|
||||||
|
|
||||||
function assertTemplate(property: string, templateRef: TemplateRef<any>| null): void {
|
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>;
|
static ɵdir: i0.ɵɵDirectiveDefWithMeta<NgForOf<any>, '[ngFor][ngForOf]', never, {'ngForOf': 'ngForOf'}, {}, never>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export declare class NgIf {
|
export declare class NgIf<T> {
|
||||||
ngIf: any;
|
ngIf: T;
|
||||||
|
ngIfElse: TemplateRef<NgIfContext<T>> | null;
|
||||||
|
ngIfThen: TemplateRef<NgIfContext<T>> | null;
|
||||||
|
constructor(_viewContainer: ViewContainerRef, templateRef: TemplateRef<NgIfContext<T>>);
|
||||||
static ngTemplateGuard_ngIf: 'binding';
|
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 {
|
export declare class CommonModule {
|
||||||
|
@ -815,6 +824,33 @@ export declare class AnimationEvent {
|
||||||
expect(diags.length).toBe(0);
|
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', () => {
|
it('should report an error with an unknown local ref target', () => {
|
||||||
env.write('test.ts', `
|
env.write('test.ts', `
|
||||||
import {Component, NgModule} from '@angular/core';
|
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';
|
import {DirectiveType, ɵɵNgOnChangesFeature, ɵɵdefineDirective, ɵɵdirectiveInject} from '../../src/render3/index';
|
||||||
|
|
||||||
export const NgForOf: DirectiveType<NgForOfDef<any, NgIterable<any>>> = NgForOfDef as any;
|
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;
|
export const NgTemplateOutlet: DirectiveType<NgTemplateOutletDef> = NgTemplateOutletDef as any;
|
||||||
|
|
||||||
NgForOf.ɵdir = ɵɵdefineDirective({
|
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);
|
constructor($implicit: T, ngForOf: U, index: number, count: number);
|
||||||
}
|
}
|
||||||
|
|
||||||
export declare class NgIf {
|
export declare class NgIf<T> {
|
||||||
ngIf: any;
|
ngIf: T;
|
||||||
ngIfElse: TemplateRef<NgIfContext> | null;
|
ngIfElse: TemplateRef<NgIfContext<T>> | null;
|
||||||
ngIfThen: TemplateRef<NgIfContext> | null;
|
ngIfThen: TemplateRef<NgIfContext<T>> | null;
|
||||||
constructor(_viewContainer: ViewContainerRef, templateRef: TemplateRef<NgIfContext>);
|
constructor(_viewContainer: ViewContainerRef, templateRef: TemplateRef<NgIfContext<T>>);
|
||||||
static ngTemplateGuard_ngIf: 'binding';
|
static ngTemplateGuard_ngIf: 'binding';
|
||||||
|
static ngTemplateContextGuard<T>(dir: NgIf<T>, ctx: any): ctx is NgIfContext<T>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export declare class NgIfContext {
|
export declare class NgIfContext<T> {
|
||||||
$implicit: any;
|
$implicit: T;
|
||||||
ngIf: any;
|
ngIf: T;
|
||||||
}
|
}
|
||||||
|
|
||||||
export declare class NgLocaleLocalization extends NgLocalization {
|
export declare class NgLocaleLocalization extends NgLocalization {
|
||||||
|
|
Loading…
Reference in New Issue