Skip to content
Permalink
Browse files

feat(language-service): completions support for tuple array (#33928)

PR Close #33928
  • Loading branch information
ivanwonder authored and mhevery committed Nov 20, 2019
1 parent 7402430 commit 7faa9bbc093ee466d62d3020c594d4b7764c9d93
@@ -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,15 +284,22 @@ 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;

const typeKind = typeKindOf(type.tsType);
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}}}`);
@@ -158,6 +158,23 @@ 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)">
</test-comp>`);
const diags = ngLS.getDiagnostics(TEST_TEMPLATE);
expect(diags).toEqual([]);
});

describe('in expression-cases.ts', () => {
it('should report access to an unknown field', () => {
const diags = ngLS.getDiagnostics(EXPRESSION_CASES).map(d => d.messageText);
@@ -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;

0 comments on commit 7faa9bb

Please sign in to comment.
You can’t perform that action at this time.