diff --git a/packages/compiler-cli/src/ngtsc/program.ts b/packages/compiler-cli/src/ngtsc/program.ts index aa9c343a77..1ec33392c0 100644 --- a/packages/compiler-cli/src/ngtsc/program.ts +++ b/packages/compiler-cli/src/ngtsc/program.ts @@ -423,7 +423,7 @@ export class NgtscProgram implements api.Program { if (this.options.fullTemplateTypeCheck) { const strictTemplates = !!this.options.strictTemplates; typeCheckingConfig = { - applyTemplateContextGuards: true, + applyTemplateContextGuards: strictTemplates, checkQueries: false, checkTemplateBodies: true, checkTypeOfInputBindings: strictTemplates, @@ -468,6 +468,7 @@ export class NgtscProgram implements api.Program { // based on "fullTemplateTypeCheck". if (this.options.strictInputTypes !== undefined) { typeCheckingConfig.checkTypeOfInputBindings = this.options.strictInputTypes; + typeCheckingConfig.applyTemplateContextGuards = this.options.strictInputTypes; } if (this.options.strictNullInputTypes !== undefined) { typeCheckingConfig.strictNullInputBindings = this.options.strictNullInputTypes; diff --git a/packages/compiler-cli/test/ngtsc/template_typecheck_spec.ts b/packages/compiler-cli/test/ngtsc/template_typecheck_spec.ts index d583fc1e0d..1f41873e0b 100644 --- a/packages/compiler-cli/test/ngtsc/template_typecheck_spec.ts +++ b/packages/compiler-cli/test/ngtsc/template_typecheck_spec.ts @@ -43,6 +43,16 @@ export declare class IndexPipe { static ɵpipe: i0.ɵPipeDefWithMeta; } +export declare class SlicePipe { + transform(value: ReadonlyArray, start: number, end?: number): Array; + transform(value: string, start: number, end?: number): string; + transform(value: null, start: number, end?: number): null; + transform(value: undefined, start: number, end?: number): undefined; + transform(value: any, start: number, end?: number): any; + + static ɵpipe: i0.ɵPipeDefWithMeta; +} + export declare class NgForOf { ngForOf: i0.NgIterable; static ngTemplateContextGuard(dir: NgForOf, ctx: any): ctx is NgForOfContext; @@ -56,7 +66,7 @@ export declare class NgIf { } export declare class CommonModule { - static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵmod: i0.ɵɵNgModuleDefWithMeta; } `); env.write('node_modules/@angular/animations/index.d.ts', ` @@ -862,30 +872,48 @@ export declare class AnimationEvent { expect(diags[0].length).toBe(19); }); - it('should property type-check a microsyntax variable with the same name as the expression', - () => { - env.write('test.ts', ` - import {CommonModule} from '@angular/common'; - import {Component, Input, NgModule} from '@angular/core'; + describe('microsyntax variables', () => { + beforeEach(() => { + // Use the same template for both tests + env.write('test.ts', ` + import {CommonModule} from '@angular/common'; + import {Component, NgModule} from '@angular/core'; + + @Component({ + selector: 'test', + template: \`
+ {{foo.name}} of {{foos.length}} +
+ \`, + }) + export class TestCmp { + foos: {name: string}[]; + } + + @NgModule({ + declarations: [TestCmp], + imports: [CommonModule], + }) + export class Module {} + `); + }); - @Component({ - selector: 'test', - template: '
{{foo}}
', - }) - export class TestCmp { - foo: any; - } + it('should be treated as \'any\' without strictTemplates', () => { + env.tsconfig({fullTemplateTypeCheck: true, strictTemplates: false}); - @NgModule({ - declarations: [TestCmp], - imports: [CommonModule], - }) - export class Module {} - `); + const diags = env.driveDiagnostics(); + expect(diags.length).toBe(0); + }); - const diags = env.driveDiagnostics(); - expect(diags.length).toBe(0); - }); + it('should be correctly inferred under strictTemplates', () => { + env.tsconfig({fullTemplateTypeCheck: true, strictTemplates: true}); + + const diags = env.driveDiagnostics(); + expect(diags.length).toBe(1); + expect((diags[0].messageText as ts.DiagnosticMessageChain).messageText) + .toBe(`Property 'length' does not exist on type 'NgIterable<{ name: string; }>'.`); + }); + }); it('should properly type-check inherited directives', () => { env.tsconfig({fullTemplateTypeCheck: true, strictInputTypes: true});