diff --git a/packages/language-service/src/typescript_symbols.ts b/packages/language-service/src/typescript_symbols.ts
index 69812f295f..f018a092c6 100644
--- a/packages/language-service/src/typescript_symbols.ts
+++ b/packages/language-service/src/typescript_symbols.ts
@@ -127,10 +127,12 @@ class TypeScriptSymbolQuery implements SymbolQuery {
getElementType(type: Symbol): Symbol|undefined {
if (type instanceof TypeWrapper) {
- const tSymbol = type.tsType.symbol;
- const tArgs = type.typeArguments();
- if (!tSymbol || tSymbol.name !== 'Array' || !tArgs || tArgs.length != 1) return;
- return tArgs[0];
+ const ty = type.tsType;
+ const tyArgs = type.typeArguments();
+ // TODO(ayazhafiz): Track https://github.com/microsoft/TypeScript/issues/37711 to expose
+ // `isArrayLikeType` as a public method.
+ if (!(this.checker as any).isArrayLikeType(ty) || tyArgs?.length !== 1) return;
+ return tyArgs[0];
}
}
diff --git a/packages/language-service/test/hover_spec.ts b/packages/language-service/test/hover_spec.ts
index dc3bb7098b..9a22801c06 100644
--- a/packages/language-service/test/hover_spec.ts
+++ b/packages/language-service/test/hover_spec.ts
@@ -96,15 +96,47 @@ describe('hover', () => {
expect(documentation).toBe('This is the title of the `TemplateReference` Component.');
});
- it('should work for property reads', () => {
- mockHost.override(TEST_TEMPLATE, `
{{«title»}}
`);
- const marker = mockHost.getReferenceMarkerFor(TEST_TEMPLATE, 'title');
- const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start);
- expect(quickInfo).toBeTruthy();
- const {textSpan, displayParts} = quickInfo !;
- expect(textSpan).toEqual(marker);
- expect(textSpan.length).toBe('title'.length);
- expect(toText(displayParts)).toBe('(property) TemplateReference.title: string');
+ describe('property reads', () => {
+ it('should work for class members', () => {
+ mockHost.override(TEST_TEMPLATE, `{{«title»}}
`);
+ const marker = mockHost.getReferenceMarkerFor(TEST_TEMPLATE, 'title');
+ const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start);
+ expect(quickInfo).toBeTruthy();
+ const {textSpan, displayParts} = quickInfo !;
+ expect(textSpan).toEqual(marker);
+ expect(toText(displayParts)).toBe('(property) TemplateReference.title: string');
+ });
+
+ it('should work for array members', () => {
+ mockHost.override(TEST_TEMPLATE, `{{«hero»}}
`);
+ const marker = mockHost.getReferenceMarkerFor(TEST_TEMPLATE, 'hero');
+ const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start);
+ expect(quickInfo).toBeTruthy();
+ const {textSpan, displayParts} = quickInfo !;
+ expect(textSpan).toEqual(marker);
+ expect(toText(displayParts)).toBe('(variable) hero: Hero');
+ });
+
+ it('should work for ReadonlyArray members (#36191)', () => {
+ mockHost.override(
+ TEST_TEMPLATE, `{{«hero»}}
`);
+ const marker = mockHost.getReferenceMarkerFor(TEST_TEMPLATE, 'hero');
+ const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start);
+ expect(quickInfo).toBeTruthy();
+ const {textSpan, displayParts} = quickInfo !;
+ expect(textSpan).toEqual(marker);
+ expect(toText(displayParts)).toBe('(variable) hero: Readonly');
+ });
+
+ it('should work for const array members (#36191)', () => {
+ mockHost.override(TEST_TEMPLATE, `{{«name»}}
`);
+ const marker = mockHost.getReferenceMarkerFor(TEST_TEMPLATE, 'name');
+ const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start);
+ expect(quickInfo).toBeTruthy();
+ const {textSpan, displayParts} = quickInfo !;
+ expect(textSpan).toEqual(marker);
+ expect(toText(displayParts)).toBe('(variable) name: { readonly name: "name"; }');
+ });
});
it('should work for method calls', () => {
diff --git a/packages/language-service/test/project/app/parsing-cases.ts b/packages/language-service/test/project/app/parsing-cases.ts
index 1d5c01c70e..3239e008cf 100644
--- a/packages/language-service/test/project/app/parsing-cases.ts
+++ b/packages/language-service/test/project/app/parsing-cases.ts
@@ -174,6 +174,8 @@ export class TemplateReference {
index = null;
myClick(event: any) {}
birthday = new Date();
+ readonlyHeroes: ReadonlyArray> = this.heroes;
+ constNames = [{name: 'name'}] as const ;
}
@Component({