diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/type_check_block.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/type_check_block.ts index 705de13bf6e79..1776122371050 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/type_check_block.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/type_check_block.ts @@ -1276,7 +1276,7 @@ function tcbCreateEventHandler( /* modifier */ undefined, /* typeParameters */ undefined, /* parameters */[eventParam], - /* type */ undefined, + /* type */ ts.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword), /* equalsGreaterThanToken*/ undefined, /* body */ handler); } diff --git a/packages/compiler-cli/src/ngtsc/typecheck/test/diagnostics_spec.ts b/packages/compiler-cli/src/ngtsc/typecheck/test/diagnostics_spec.ts index 8cedeb585e98b..b00c2efd65918 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/test/diagnostics_spec.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/test/diagnostics_spec.ts @@ -6,9 +6,11 @@ * found in the LICENSE file at https://angular.io/license */ -import {TestFile, runInEachFileSystem} from '@angular/compiler-cli/src/ngtsc/file_system/testing'; import * as ts from 'typescript'; +import {TestFile, runInEachFileSystem} from '../../file_system/testing'; +import {TypeCheckingConfig} from '../src/api'; + import {TestDeclaration, ngForDeclaration, ngForDts, typecheck} from './test_utils'; runInEachFileSystem(() => { @@ -258,6 +260,21 @@ runInEachFileSystem(() => { expect(messages).toEqual([]); }); + + // https://github.com/angular/angular/issues/33528 + it('should not produce a diagnostic for implicit any return types', () => { + const messages = diagnose( + `
`, ` + class TestComponent { + state: any; + }`, + // Disable strict DOM event checking and strict null checks, to infer an any return type + // that would be implicit if the handler function would not have an explicit return + // type. + [], [], {checkTypeOfDomEvents: false}, {strictNullChecks: false}); + + expect(messages).toEqual([]); + }); }); describe('strict null checks', () => { @@ -314,8 +331,9 @@ class TestComponent { function diagnose( template: string, source: string, declarations?: TestDeclaration[], - additionalSources: TestFile[] = []): string[] { - const diagnostics = typecheck(template, source, declarations, additionalSources); + additionalSources: TestFile[] = [], config?: Partial, + options?: ts.CompilerOptions): string[] { + const diagnostics = typecheck(template, source, declarations, additionalSources, config, options); return diagnostics.map(diag => { const text = typeof diag.messageText === 'string' ? diag.messageText : diag.messageText.messageText; diff --git a/packages/compiler-cli/src/ngtsc/typecheck/test/test_utils.ts b/packages/compiler-cli/src/ngtsc/typecheck/test/test_utils.ts index 76962ea6a1754..5d816acb4a7a3 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/test/test_utils.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/test/test_utils.ts @@ -230,7 +230,8 @@ export function tcb( export function typecheck( template: string, source: string, declarations: TestDeclaration[] = [], - additionalSources: {name: AbsoluteFsPath; contents: string}[] = []): ts.Diagnostic[] { + additionalSources: {name: AbsoluteFsPath; contents: string}[] = [], + config: Partial = {}, opts: ts.CompilerOptions = {}): ts.Diagnostic[] { const typeCheckFilePath = absoluteFrom('/_typecheck_.ts'); const files = [ typescriptLibDts(), @@ -243,7 +244,7 @@ export function typecheck( ...additionalSources, ]; const {program, host, options} = - makeProgram(files, {strictNullChecks: true, noImplicitAny: true}, undefined, false); + makeProgram(files, {strictNullChecks: true, noImplicitAny: true, ...opts}, undefined, false); const sf = program.getSourceFile(absoluteFrom('/main.ts')) !; const checker = program.getTypeChecker(); const logicalFs = new LogicalFileSystem(getRootDirs(host, options)); @@ -254,7 +255,7 @@ export function typecheck( program, checker, options, host, new TypeScriptReflectionHost(checker)), new LogicalProjectStrategy(reflectionHost, logicalFs), ]); - const ctx = new TypeCheckContext(ALL_ENABLED_CONFIG, emitter, typeCheckFilePath); + const ctx = new TypeCheckContext({...ALL_ENABLED_CONFIG, ...config}, emitter, typeCheckFilePath); const templateUrl = 'synthetic.html'; const templateFile = new ParseSourceFile(template, templateUrl); diff --git a/packages/compiler-cli/src/ngtsc/typecheck/test/type_check_block_spec.ts b/packages/compiler-cli/src/ngtsc/typecheck/test/type_check_block_spec.ts index 9343450425311..f1a22fcffdfff 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/test/type_check_block_spec.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/test/type_check_block_spec.ts @@ -253,26 +253,26 @@ describe('type check blocks', () => { const TEMPLATE = `
`; const block = tcb(TEMPLATE, DIRECTIVES); expect(block).toContain( - '_outputHelper(_t2.outputField).subscribe($event => (ctx).foo($event));'); + '_outputHelper(_t2.outputField).subscribe(($event): any => (ctx).foo($event));'); }); it('should emit a listener function with AnimationEvent for animation events', () => { const TEMPLATE = `
`; const block = tcb(TEMPLATE); - expect(block).toContain('($event: animations.AnimationEvent) => (ctx).foo($event);'); + expect(block).toContain('($event: animations.AnimationEvent): any => (ctx).foo($event);'); }); it('should emit addEventListener calls for unclaimed outputs', () => { const TEMPLATE = `
`; const block = tcb(TEMPLATE); - expect(block).toContain('_t1.addEventListener("event", $event => (ctx).foo($event));'); + expect(block).toContain('_t1.addEventListener("event", ($event): any => (ctx).foo($event));'); }); it('should allow to cast $event using $any', () => { const TEMPLATE = `
`; const block = tcb(TEMPLATE); expect(block).toContain( - '_t1.addEventListener("event", $event => (ctx).foo(($event as any)));'); + '_t1.addEventListener("event", ($event): any => (ctx).foo(($event as any)));'); }); }); @@ -374,18 +374,18 @@ describe('type check blocks', () => { it('should check types of directive outputs when enabled', () => { const block = tcb(TEMPLATE, DIRECTIVES); expect(block).toContain( - '_outputHelper(_t2.outputField).subscribe($event => (ctx).foo($event));'); + '_outputHelper(_t2.outputField).subscribe(($event): any => (ctx).foo($event));'); expect(block).toContain( - '_t1.addEventListener("nonDirOutput", $event => (ctx).foo($event));'); + '_t1.addEventListener("nonDirOutput", ($event): any => (ctx).foo($event));'); }); it('should not check types of directive outputs when disabled', () => { const DISABLED_CONFIG: TypeCheckingConfig = {...BASE_CONFIG, checkTypeOfOutputEvents: false}; const block = tcb(TEMPLATE, DIRECTIVES, DISABLED_CONFIG); - expect(block).toContain('($event: any) => (ctx).foo($event);'); + expect(block).toContain('($event: any): any => (ctx).foo($event);'); // Note that DOM events are still checked, that is controlled by `checkTypeOfDomEvents` expect(block).toContain( - '_t1.addEventListener("nonDirOutput", $event => (ctx).foo($event));'); + '_t1.addEventListener("nonDirOutput", ($event): any => (ctx).foo($event));'); }); }); @@ -394,13 +394,13 @@ describe('type check blocks', () => { it('should check types of animation events when enabled', () => { const block = tcb(TEMPLATE, DIRECTIVES); - expect(block).toContain('($event: animations.AnimationEvent) => (ctx).foo($event);'); + expect(block).toContain('($event: animations.AnimationEvent): any => (ctx).foo($event);'); }); it('should not check types of animation events when disabled', () => { const DISABLED_CONFIG: TypeCheckingConfig = {...BASE_CONFIG, checkTypeOfAnimationEvents: false}; const block = tcb(TEMPLATE, DIRECTIVES, DISABLED_CONFIG); - expect(block).toContain('($event: any) => (ctx).foo($event);'); + expect(block).toContain('($event: any): any => (ctx).foo($event);'); }); }); @@ -410,9 +410,9 @@ describe('type check blocks', () => { it('should check types of DOM events when enabled', () => { const block = tcb(TEMPLATE, DIRECTIVES); expect(block).toContain( - '_outputHelper(_t2.outputField).subscribe($event => (ctx).foo($event));'); + '_outputHelper(_t2.outputField).subscribe(($event): any => (ctx).foo($event));'); expect(block).toContain( - '_t1.addEventListener("nonDirOutput", $event => (ctx).foo($event));'); + '_t1.addEventListener("nonDirOutput", ($event): any => (ctx).foo($event));'); }); it('should not check types of DOM events when disabled', () => { const DISABLED_CONFIG: TypeCheckingConfig = {...BASE_CONFIG, checkTypeOfDomEvents: false}; @@ -420,8 +420,8 @@ describe('type check blocks', () => { // Note that directive outputs are still checked, that is controlled by // `checkTypeOfOutputEvents` expect(block).toContain( - '_outputHelper(_t2.outputField).subscribe($event => (ctx).foo($event));'); - expect(block).toContain('($event: any) => (ctx).foo($event);'); + '_outputHelper(_t2.outputField).subscribe(($event): any => (ctx).foo($event));'); + expect(block).toContain('($event: any): any => (ctx).foo($event);'); }); });