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 a5971b595b..4864dfaec2 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 @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {AST, BindingPipe, BindingType, BoundTarget, DYNAMIC_TYPE, ImplicitReceiver, MethodCall, ParsedEventType, ParseSourceSpan, PropertyRead, PropertyWrite, SchemaMetadata, TmplAstBoundAttribute, TmplAstBoundEvent, TmplAstBoundText, TmplAstElement, TmplAstNode, TmplAstReference, TmplAstTemplate, TmplAstTextAttribute, TmplAstVariable} from '@angular/compiler'; +import {AST, BindingPipe, BindingType, BoundTarget, DYNAMIC_TYPE, ImplicitReceiver, MethodCall, ParsedEventType, ParseSourceSpan, PropertyRead, PropertyWrite, SchemaMetadata, TmplAstBoundAttribute, TmplAstBoundEvent, TmplAstBoundText, TmplAstElement, TmplAstIcu, TmplAstNode, TmplAstReference, TmplAstTemplate, TmplAstTextAttribute, TmplAstVariable} from '@angular/compiler'; import * as ts from 'typescript'; import {Reference} from '../../imports'; @@ -1333,6 +1333,8 @@ class Scope { this.checkAndAppendReferencesOfNode(node); } else if (node instanceof TmplAstBoundText) { this.opQueue.push(new TcbTextInterpolationOp(this.tcb, this, node)); + } else if (node instanceof TmplAstIcu) { + this.appendIcuExpressions(node); } } @@ -1459,6 +1461,17 @@ class Scope { this.appendDeepSchemaChecks(node.children); } } + + private appendIcuExpressions(node: TmplAstIcu): void { + for (const variable of Object.values(node.vars)) { + this.opQueue.push(new TcbTextInterpolationOp(this.tcb, this, variable)); + } + for (const placeholder of Object.values(node.placeholders)) { + if (placeholder instanceof TmplAstBoundText) { + this.opQueue.push(new TcbTextInterpolationOp(this.tcb, this, placeholder)); + } + } + } } interface TcbBoundInput { 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 5838f2fc8e..0104b024a2 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/test/diagnostics_spec.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/test/diagnostics_spec.ts @@ -176,6 +176,21 @@ runInEachFileSystem(() => { ]); }); + it('checks expressions in ICUs', () => { + const messages = diagnose( + `{switch, plural, other { {{interpolation}} + {nestedSwitch, plural, other { {{nestedInterpolation}} }} + }}`, + `class TestComponent {}`); + + expect(messages.sort()).toEqual([ + `TestComponent.html(1, 13): Property 'switch' does not exist on type 'TestComponent'.`, + `TestComponent.html(1, 39): Property 'interpolation' does not exist on type 'TestComponent'.`, + `TestComponent.html(2, 14): Property 'nestedSwitch' does not exist on type 'TestComponent'.`, + `TestComponent.html(2, 46): Property 'nestedInterpolation' does not exist on type 'TestComponent'.`, + ]); + }); + it('produces diagnostics for pipes', () => { const messages = diagnose( `
{{ person.name | pipe:person.age:1 }}
`, ` diff --git a/packages/compiler/src/compiler.ts b/packages/compiler/src/compiler.ts index c734932b88..e60ef28312 100644 --- a/packages/compiler/src/compiler.ts +++ b/packages/compiler/src/compiler.ts @@ -92,7 +92,7 @@ export {getParseErrors, isSyntaxError, syntaxError, Version} from './util'; export {SourceMap} from './output/source_map'; export * from './injectable_compiler_2'; export * from './render3/view/api'; -export {BoundAttribute as TmplAstBoundAttribute, BoundEvent as TmplAstBoundEvent, BoundText as TmplAstBoundText, Content as TmplAstContent, Element as TmplAstElement, Node as TmplAstNode, RecursiveVisitor as TmplAstRecursiveVisitor, Reference as TmplAstReference, Template as TmplAstTemplate, Text as TmplAstText, TextAttribute as TmplAstTextAttribute, Variable as TmplAstVariable,} from './render3/r3_ast'; +export {BoundAttribute as TmplAstBoundAttribute, BoundEvent as TmplAstBoundEvent, BoundText as TmplAstBoundText, Content as TmplAstContent, Element as TmplAstElement, Icu as TmplAstIcu, Node as TmplAstNode, RecursiveVisitor as TmplAstRecursiveVisitor, Reference as TmplAstReference, Template as TmplAstTemplate, Text as TmplAstText, TextAttribute as TmplAstTextAttribute, Variable as TmplAstVariable} from './render3/r3_ast'; export * from './render3/view/t2_api'; export * from './render3/view/t2_binder'; export {Identifiers as R3Identifiers} from './render3/r3_identifiers';