feat(language-service): completions support for tuple array (#33928)
PR Close #33928
This commit is contained in:
parent
de7713d6ea
commit
5a227d8d7c
|
@ -241,7 +241,8 @@ export class AstType implements AstVisitor {
|
|||
visitKeyedRead(ast: KeyedRead): Symbol {
|
||||
const targetType = this.getType(ast.obj);
|
||||
const keyType = this.getType(ast.key);
|
||||
const result = targetType.indexed(keyType);
|
||||
const result = targetType.indexed(
|
||||
keyType, ast.key instanceof LiteralPrimitive ? ast.key.value : undefined);
|
||||
return result || this.anyType;
|
||||
}
|
||||
|
||||
|
|
|
@ -116,9 +116,12 @@ export interface Symbol {
|
|||
|
||||
/**
|
||||
* Return the type of the expression if this symbol is indexed by `argument`.
|
||||
* Sometimes we need the key of arguments to get the type of the expression, for example
|
||||
* in the case of tuples (`type Example = [string, number]`).
|
||||
* [string, number]).
|
||||
* If the symbol cannot be indexed, this method should return `undefined`.
|
||||
*/
|
||||
indexed(argument: Symbol): Symbol|undefined;
|
||||
indexed(argument: Symbol, key?: any): Symbol|undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -349,4 +352,4 @@ export interface SymbolQuery {
|
|||
* Return the span of the narrowest non-token node at the given location.
|
||||
*/
|
||||
getSpanAt(line: number, column: number): Span|undefined;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -284,7 +284,7 @@ class TypeWrapper implements Symbol {
|
|||
return selectSignature(this.tsType, this.context, types);
|
||||
}
|
||||
|
||||
indexed(argument: Symbol): Symbol|undefined {
|
||||
indexed(argument: Symbol, value: any): Symbol|undefined {
|
||||
const type = argument instanceof TypeWrapper ? argument : argument.type;
|
||||
if (!(type instanceof TypeWrapper)) return;
|
||||
|
||||
|
@ -292,7 +292,14 @@ class TypeWrapper implements Symbol {
|
|||
switch (typeKind) {
|
||||
case BuiltinType.Number:
|
||||
const nType = this.tsType.getNumberIndexType();
|
||||
return nType && new TypeWrapper(nType, this.context);
|
||||
if (nType) {
|
||||
// get the right tuple type by value, like 'var t: [number, string];'
|
||||
if (nType.isUnion()) {
|
||||
return new TypeWrapper(nType.types[value], this.context);
|
||||
}
|
||||
return new TypeWrapper(nType, this.context);
|
||||
}
|
||||
return undefined;
|
||||
case BuiltinType.String:
|
||||
const sType = this.tsType.getStringIndexType();
|
||||
return sType && new TypeWrapper(sType, this.context);
|
||||
|
|
|
@ -125,6 +125,13 @@ describe('completions', () => {
|
|||
expectContain(completions, CompletionKind.PROPERTY, ['id', 'name']);
|
||||
});
|
||||
|
||||
it('should work with numeric index signatures (tuple arrays)', () => {
|
||||
mockHost.override(TEST_TEMPLATE, `{{ tupleArray[1].~{tuple-array-number-index}}}`);
|
||||
const marker = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'tuple-array-number-index');
|
||||
const completions = ngLS.getCompletionsAt(TEST_TEMPLATE, marker.start);
|
||||
expectContain(completions, CompletionKind.PROPERTY, ['id', 'name']);
|
||||
});
|
||||
|
||||
describe('with string index signatures', () => {
|
||||
it('should work with index notation', () => {
|
||||
mockHost.override(TEST_TEMPLATE, `{{ heroesByName['Jacky'].~{heroes-string-index}}}`);
|
||||
|
|
|
@ -150,6 +150,15 @@ describe('diagnostics', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('should produce diagnostics for invalid tuple type property access', () => {
|
||||
mockHost.override(TEST_TEMPLATE, `
|
||||
{{tupleArray[1].badProperty}}`);
|
||||
const diags = ngLS.getDiagnostics(TEST_TEMPLATE);
|
||||
expect(diags.length).toBe(1);
|
||||
expect(diags[0].messageText)
|
||||
.toBe(`Identifier 'badProperty' is not defined. 'Hero' does not contain such a member`);
|
||||
});
|
||||
|
||||
it('should not produce errors on function.bind()', () => {
|
||||
mockHost.override(TEST_TEMPLATE, `
|
||||
<test-comp (test)="myClick.bind(this)">
|
||||
|
|
|
@ -189,6 +189,7 @@ export class TemplateReference {
|
|||
title = 'Some title';
|
||||
hero: Hero = {id: 1, name: 'Windstorm'};
|
||||
heroes: Hero[] = [this.hero];
|
||||
tupleArray: [string, Hero] = ['test', this.hero];
|
||||
league: Hero[][] = [this.heroes];
|
||||
heroesByName: {[name: string]: Hero} = {};
|
||||
anyValue: any;
|
||||
|
|
Loading…
Reference in New Issue