diff --git a/packages/compiler-cli/src/ngtsc/typecheck/test/span_comments_spec.ts b/packages/compiler-cli/src/ngtsc/typecheck/test/span_comments_spec.ts index 231a1e1712..0c153b47f7 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/test/span_comments_spec.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/test/span_comments_spec.ts @@ -129,6 +129,13 @@ describe('type check blocks diagnostics', () => { '(null as any ? (((ctx).a /*3,4*/) /*3,4*/)!.method /*6,12*/(((ctx).b /*13,14*/) /*13,14*/) : undefined) /*3,15*/'); }); + it('should annotate safe keyed reads', () => { + const TEMPLATE = `{{ a?.[0] }}`; + expect(tcbWithSpans(TEMPLATE)) + .toContain( + '(null as any ? (((ctx).a /*3,4*/) /*3,4*/)![0 /*7,8*/] /*3,9*/ : undefined) /*3,9*/'); + }); + it('should annotate $any casts', () => { const TEMPLATE = `{{ $any(a) }}`; expect(tcbWithSpans(TEMPLATE)).toContain('(((ctx).a /*8,9*/) /*8,9*/ as any) /*3,10*/'); diff --git a/packages/compiler-cli/src/ngtsc/typecheck/test/type_checker__get_symbol_of_template_node_spec.ts b/packages/compiler-cli/src/ngtsc/typecheck/test/type_checker__get_symbol_of_template_node_spec.ts index 429a403f3e..290da7d875 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/test/type_checker__get_symbol_of_template_node_spec.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/test/type_checker__get_symbol_of_template_node_spec.ts @@ -394,6 +394,7 @@ runInEachFileSystem(() => {
+
`; const testValues = setup( [ @@ -405,9 +406,14 @@ runInEachFileSystem(() => { street: string; } + interface Car { + engine: string; + } + interface Person { address: Address; speak(): string; + cars?: Car[]; } export class Cmp {person?: Person; noPersonError = 'no person'} `, @@ -448,6 +454,19 @@ runInEachFileSystem(() => { .toEqual('string | undefined'); }); + it('safe keyed reads', () => { + const nodes = getAstElements(templateTypeChecker, cmp); + const safeKeyedRead = nodes[3].inputs[0].value as ASTWithSource; + const keyedReadSymbol = templateTypeChecker.getSymbolOfNode(safeKeyedRead, cmp)!; + assertExpressionSymbol(keyedReadSymbol); + expect(program.getTypeChecker().symbolToString(keyedReadSymbol.tsSymbol!)) + .toEqual('engine'); + expect((keyedReadSymbol.tsSymbol!.declarations![0] as ts.PropertyDeclaration) + .parent.name!.getText()) + .toEqual('Car'); + expect(program.getTypeChecker().typeToString(keyedReadSymbol.tsType)).toEqual('string'); + }); + it('ternary expressions', () => { const nodes = getAstElements(templateTypeChecker, cmp); diff --git a/packages/language-service/ivy/test/quick_info_spec.ts b/packages/language-service/ivy/test/quick_info_spec.ts index c8a1f0b9a7..a23b9dd350 100644 --- a/packages/language-service/ivy/test/quick_info_spec.ts +++ b/packages/language-service/ivy/test/quick_info_spec.ts @@ -357,6 +357,20 @@ describe('quick info', () => { expectedDisplayString: '(variable) name: { readonly name: "name"; }' }); }); + + it('should work for safe keyed reads', () => { + expectQuickInfo({ + templateOverride: `
{{constNames?.[0¦]}}
`, + expectedSpanText: '0', + expectedDisplayString: '(property) 0: {\n readonly name: "name";\n}' + }); + + expectQuickInfo({ + templateOverride: `
{{constNames?.[0]?.na¦me}}
`, + expectedSpanText: 'constNames?.[0]?.name', + expectedDisplayString: '(property) name: "name"' + }); + }); }); describe('pipes', () => {