Skip to content

Commit 0a16e60

Browse files
JoostKatscott
authored andcommitted
fix(compiler-cli): type checking of expressions within ICUs (#39072)
Expressions within ICU expressions in templates were not previously type-checked, as they were skipped while traversing the elements within a template. This commit enables type checking of these expressions by actually visiting the expressions. BREAKING CHANGE: Expressions within ICUs are now type-checked again, fixing a regression in Ivy. This may cause compilation failures if errors are found in expressions that appear within an ICU. Please correct these expressions to resolve the type-check errors. Fixes #39064 PR Close #39072
1 parent f84b378 commit 0a16e60

File tree

3 files changed

+30
-2
lines changed

3 files changed

+30
-2
lines changed

packages/compiler-cli/src/ngtsc/typecheck/src/type_check_block.ts

+14-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
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';
9+
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';
1010
import * as ts from 'typescript';
1111

1212
import {Reference} from '../../imports';
@@ -1333,6 +1333,8 @@ class Scope {
13331333
this.checkAndAppendReferencesOfNode(node);
13341334
} else if (node instanceof TmplAstBoundText) {
13351335
this.opQueue.push(new TcbTextInterpolationOp(this.tcb, this, node));
1336+
} else if (node instanceof TmplAstIcu) {
1337+
this.appendIcuExpressions(node);
13361338
}
13371339
}
13381340

@@ -1459,6 +1461,17 @@ class Scope {
14591461
this.appendDeepSchemaChecks(node.children);
14601462
}
14611463
}
1464+
1465+
private appendIcuExpressions(node: TmplAstIcu): void {
1466+
for (const variable of Object.values(node.vars)) {
1467+
this.opQueue.push(new TcbTextInterpolationOp(this.tcb, this, variable));
1468+
}
1469+
for (const placeholder of Object.values(node.placeholders)) {
1470+
if (placeholder instanceof TmplAstBoundText) {
1471+
this.opQueue.push(new TcbTextInterpolationOp(this.tcb, this, placeholder));
1472+
}
1473+
}
1474+
}
14621475
}
14631476

14641477
interface TcbBoundInput {

packages/compiler-cli/src/ngtsc/typecheck/test/diagnostics_spec.ts

+15
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,21 @@ runInEachFileSystem(() => {
176176
]);
177177
});
178178

179+
it('checks expressions in ICUs', () => {
180+
const messages = diagnose(
181+
`<span i18n>{switch, plural, other { {{interpolation}}
182+
{nestedSwitch, plural, other { {{nestedInterpolation}} }}
183+
}}</span>`,
184+
`class TestComponent {}`);
185+
186+
expect(messages.sort()).toEqual([
187+
`TestComponent.html(1, 13): Property 'switch' does not exist on type 'TestComponent'.`,
188+
`TestComponent.html(1, 39): Property 'interpolation' does not exist on type 'TestComponent'.`,
189+
`TestComponent.html(2, 14): Property 'nestedSwitch' does not exist on type 'TestComponent'.`,
190+
`TestComponent.html(2, 46): Property 'nestedInterpolation' does not exist on type 'TestComponent'.`,
191+
]);
192+
});
193+
179194
it('produces diagnostics for pipes', () => {
180195
const messages = diagnose(
181196
`<div>{{ person.name | pipe:person.age:1 }}</div>`, `

packages/compiler/src/compiler.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ export {getParseErrors, isSyntaxError, syntaxError, Version} from './util';
9292
export {SourceMap} from './output/source_map';
9393
export * from './injectable_compiler_2';
9494
export * from './render3/view/api';
95-
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';
95+
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';
9696
export * from './render3/view/t2_api';
9797
export * from './render3/view/t2_binder';
9898
export {Identifiers as R3Identifiers} from './render3/r3_identifiers';

0 commit comments

Comments
 (0)