Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(language-service): bug of accessing a string index signature using dot notation #34177

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
29 changes: 12 additions & 17 deletions packages/language-service/src/typescript_symbols.ts
Expand Up @@ -244,10 +244,7 @@ class TypeWrapper implements Symbol {
}
}

get name(): string {
const symbol = this.tsType.symbol;
return (symbol && symbol.name) || '<anonymous>';
}
get name(): string { return this.context.checker.typeToString(this.tsType); }

public readonly kind: DeclarationKind = 'type';

Expand Down Expand Up @@ -308,6 +305,12 @@ class TypeWrapper implements Symbol {
}
}

// If stringIndexType a primitive type(e.g. 'string'), the Symbol is undefined;
// and in AstType.resolvePropertyRead method, the Symbol.type should get the right type.
class StringIndexTypeWrapper extends TypeWrapper {
public readonly type = new TypeWrapper(this.tsType, this.context);
}

class SymbolWrapper implements Symbol {
private symbol: ts.Symbol;
private _members?: SymbolTable;
Expand Down Expand Up @@ -506,10 +509,7 @@ class SymbolTableWrapper implements SymbolTable {
// obj.stringIndex // equivalent to obj['stringIndex'];
//
// In this case, return the type indexed by an arbitrary string key.
const symbol = this.stringIndexType.getSymbol();
if (symbol) {
return new SymbolWrapper(symbol, this.context, this.stringIndexType);
}
return new StringIndexTypeWrapper(this.stringIndexType, this.context);
}

return undefined;
Expand Down Expand Up @@ -608,15 +608,10 @@ class PipeSymbol implements Symbol {
let resultType: ts.Type|undefined = undefined;
switch (this.name) {
case 'async':
switch (parameterType.name) {
case 'Observable':
case 'Promise':
case 'EventEmitter':
resultType = getTypeParameterOf(parameterType.tsType, parameterType.name);
break;
default:
resultType = getTsTypeFromBuiltinType(BuiltinType.Any, this.context);
break;
// Get symbol of 'Observable', 'Promise', or 'EventEmitter' type.
const symbol = parameterType.tsType.symbol;
if (symbol) {
resultType = getTypeParameterOf(parameterType.tsType, symbol.name);
}
break;
case 'slice':
Expand Down
7 changes: 7 additions & 0 deletions packages/language-service/test/completions_spec.ts
Expand Up @@ -146,6 +146,13 @@ describe('completions', () => {
const completions = ngLS.getCompletionsAt(TEST_TEMPLATE, marker.start);
expectContain(completions, CompletionKind.PROPERTY, ['id', 'name']);
});

it('should work with dot notation if stringIndexType is a primitive type', () => {
mockHost.override(TEST_TEMPLATE, `{{ primitiveIndexType.test.~{string-primitive-type}}}`);
const marker = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'string-primitive-type');
const completions = ngLS.getCompletionsAt(TEST_TEMPLATE, marker.start);
expectContain(completions, CompletionKind.METHOD, ['substring']);
});
});
});

Expand Down
7 changes: 7 additions & 0 deletions packages/language-service/test/diagnostics_spec.ts
Expand Up @@ -147,6 +147,13 @@ describe('diagnostics', () => {
expect(diags[0].messageText)
.toBe(`Identifier 'badProperty' is not defined. 'Hero' does not contain such a member`);
});

it('should not produce errors with dot notation if stringIndexType is a primitive type',
() => {
mockHost.override(TEST_TEMPLATE, `{{primitiveIndexType.test}}`);
const diags = ngLS.getDiagnostics(TEST_TEMPLATE);
expect(diags.length).toBe(0);
});
});
});

Expand Down
Expand Up @@ -192,6 +192,7 @@ export class TemplateReference {
tupleArray: [string, Hero] = ['test', this.hero];
league: Hero[][] = [this.heroes];
heroesByName: {[name: string]: Hero} = {};
primitiveIndexType: {[name: string]: string} = {};
anyValue: any;
myClick(event: any) {}
}
Expand Down