diff --git a/packages/compiler-cli/src/ngtsc/program.ts b/packages/compiler-cli/src/ngtsc/program.ts index 2e9eddafc2..c4914f86ab 100644 --- a/packages/compiler-cli/src/ngtsc/program.ts +++ b/packages/compiler-cli/src/ngtsc/program.ts @@ -435,6 +435,7 @@ export class NgtscProgram implements api.Program { // - error TS2531: Object is possibly 'null'. // - error TS2339: Property 'value' does not exist on type 'EventTarget'. checkTypeOfDomEvents: false, + checkTypeOfReferences: true, checkTypeOfPipes: true, strictSafeNavigationTypes: true, }; @@ -449,6 +450,7 @@ export class NgtscProgram implements api.Program { checkTypeOfOutputEvents: false, checkTypeOfAnimationEvents: false, checkTypeOfDomEvents: false, + checkTypeOfReferences: false, checkTypeOfPipes: false, strictSafeNavigationTypes: false, }; diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/api.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/api.ts index 4c9138d4a3..a74e030a5b 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/api.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/api.ts @@ -133,6 +133,15 @@ export interface TypeCheckingConfig { */ checkTypeOfDomEvents: boolean; + /** + * Whether to infer the type of local references. + * + * If this is `true`, the type of any `#ref` variable in the template will be determined by the + * referenced entity (either a directive or a DOM element). If set to `false`, the type of `ref` + * will be `any`. + */ + checkTypeOfReferences: boolean; + /** * Whether to include type information from pipes in the type-checking operation. * 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 49484e9a20..755f2f679b 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 @@ -954,6 +954,11 @@ class TcbExpressionTranslator { addParseSpanInfo(expr, toAbsoluteSpan(ast.span, this.sourceSpan)); return expr; } else if (binding instanceof TmplAstReference) { + if (!this.tcb.env.config.checkTypeOfReferences) { + // References are pinned to 'any'. + return NULL_AS_ANY; + } + const target = this.tcb.boundTarget.getReferenceTarget(binding); if (target === null) { throw new Error(`Unbound reference? ${binding.name}`); 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 9d443eb2bd..e6bca0d025 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/test/test_utils.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/test/test_utils.ts @@ -155,6 +155,7 @@ export const ALL_ENABLED_CONFIG: TypeCheckingConfig = { checkTypeOfOutputEvents: true, checkTypeOfAnimationEvents: true, checkTypeOfDomEvents: true, + checkTypeOfReferences: true, checkTypeOfPipes: true, strictSafeNavigationTypes: true, }; @@ -198,6 +199,7 @@ export function tcb( checkTypeOfOutputEvents: true, checkTypeOfAnimationEvents: true, checkTypeOfDomEvents: true, + checkTypeOfReferences: true, checkTypeOfPipes: true, checkTemplateBodies: true, strictSafeNavigationTypes: true, 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 030395fb41..076d8455d8 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 @@ -291,6 +291,7 @@ describe('type check blocks', () => { checkTypeOfOutputEvents: true, checkTypeOfAnimationEvents: true, checkTypeOfDomEvents: true, + checkTypeOfReferences: true, checkTypeOfPipes: true, strictSafeNavigationTypes: true, }; @@ -416,6 +417,20 @@ describe('type check blocks', () => { }); }); + describe('config.checkTypeOfReferences', () => { + const TEMPLATE = `{{ref.value}}`; + + it('should trace references when enabled', () => { + const block = tcb(TEMPLATE); + expect(block).toContain('(_t1).value'); + }); + + it('should use any for reference types when disabled', () => { + const DISABLED_CONFIG: TypeCheckingConfig = {...BASE_CONFIG, checkTypeOfReferences: false}; + const block = tcb(TEMPLATE, [], DISABLED_CONFIG); + expect(block).toContain('(null as any).value'); + }); + }); describe('config.checkTypeOfPipes', () => { const TEMPLATE = `{{a | test:b:c}}`;