diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index 76e11d53811..b77b5a62177 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -432,6 +432,7 @@ namespace ts {
getIndexInfosOfType,
getSignaturesOfType,
getIndexTypeOfType: (type, kind) => getIndexTypeOfType(type, kind === IndexKind.String ? stringType : numberType),
+ getIndexType: type => getIndexType(type),
getBaseTypes,
getBaseTypeOfLiteralType,
getWidenedType,
@@ -15344,10 +15345,6 @@ namespace ts {
(type.flags & (TypeFlags.InstantiableNonPrimitive | TypeFlags.Index | TypeFlags.TemplateLiteral | TypeFlags.StringMapping) && !isPatternLiteralType(type) ? ObjectFlags.IsGenericIndexType : 0);
}
- function isThisTypeParameter(type: Type): boolean {
- return !!(type.flags & TypeFlags.TypeParameter && (type as TypeParameter).isThisType);
- }
-
function getSimplifiedType(type: Type, writing: boolean): Type {
return type.flags & TypeFlags.IndexedAccess ? getSimplifiedIndexedAccessType(type as IndexedAccessType, writing) :
type.flags & TypeFlags.Conditional ? getSimplifiedConditionalType(type as ConditionalType, writing) :
diff --git a/src/compiler/types.ts b/src/compiler/types.ts
index bdff510977b..e7e60dc0252 100644
--- a/src/compiler/types.ts
+++ b/src/compiler/types.ts
@@ -4162,6 +4162,7 @@ namespace ts {
getIndexInfosOfType(type: Type): readonly IndexInfo[];
getSignaturesOfType(type: Type, kind: SignatureKind): readonly Signature[];
getIndexTypeOfType(type: Type, kind: IndexKind): Type | undefined;
+ /* @internal */ getIndexType(type: Type): Type;
getBaseTypes(type: InterfaceType): BaseType[];
getBaseTypeOfLiteralType(type: Type): Type;
getWidenedType(type: Type): Type;
diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts
index a24f61a93fa..72ce071940d 100644
--- a/src/compiler/utilities.ts
+++ b/src/compiler/utilities.ts
@@ -7414,4 +7414,8 @@ namespace ts {
export function escapeSnippetText(text: string): string {
return text.replace(/\$/gm, "\\$");
}
+
+ export function isThisTypeParameter(type: Type): boolean {
+ return !!(type.flags & TypeFlags.TypeParameter && (type as TypeParameter).isThisType);
+ }
}
diff --git a/src/services/services.ts b/src/services/services.ts
index c9c54c0234e..c65f5221686 100644
--- a/src/services/services.ts
+++ b/src/services/services.ts
@@ -500,6 +500,9 @@ namespace ts {
isClass(): this is InterfaceType {
return !!(getObjectFlags(this) & ObjectFlags.Class);
}
+ isIndexType(): this is IndexType {
+ return !!(this.flags & TypeFlags.Index);
+ }
/**
* This polyfills `referenceType.typeArguments` for API consumers
*/
@@ -545,6 +548,16 @@ namespace ts {
getReturnType(): Type {
return this.checker.getReturnTypeOfSignature(this);
}
+ getTypeParameterAtPosition(pos: number): Type {
+ const type = this.checker.getParameterType(this, pos);
+ if (type.isIndexType() && isThisTypeParameter(type.type)) {
+ const constraint = type.type.getConstraint();
+ if (constraint) {
+ return this.checker.getIndexType(constraint);
+ }
+ }
+ return type;
+ }
getDocumentationComment(): SymbolDisplayPart[] {
return this.documentationComment || (this.documentationComment = getDocumentationComment(singleElementArray(this.declaration), this.checker));
diff --git a/src/services/stringCompletions.ts b/src/services/stringCompletions.ts
index 2c1a453df6d..b84b578ed4f 100644
--- a/src/services/stringCompletions.ts
+++ b/src/services/stringCompletions.ts
@@ -264,7 +264,7 @@ namespace ts.Completions.StringCompletions {
checker.getResolvedSignature(argumentInfo.invocation, candidates, argumentInfo.argumentCount);
const types = flatMap(candidates, candidate => {
if (!signatureHasRestParameter(candidate) && argumentInfo.argumentCount > candidate.parameters.length) return;
- const type = checker.getParameterType(candidate, argumentInfo.argumentIndex);
+ const type = candidate.getTypeParameterAtPosition(argumentInfo.argumentIndex);
isNewIdentifier = isNewIdentifier || !!(type.flags & TypeFlags.String);
return getStringLiteralTypes(type, uniques);
});
diff --git a/src/services/types.ts b/src/services/types.ts
index 0659e6295e6..b9bd9f2177d 100644
--- a/src/services/types.ts
+++ b/src/services/types.ts
@@ -72,6 +72,7 @@ namespace ts {
isTypeParameter(): this is TypeParameter;
isClassOrInterface(): this is InterfaceType;
isClass(): this is InterfaceType;
+ isIndexType(): this is IndexType;
}
export interface TypeReference {
@@ -82,6 +83,7 @@ namespace ts {
getDeclaration(): SignatureDeclaration;
getTypeParameters(): TypeParameter[] | undefined;
getParameters(): Symbol[];
+ getTypeParameterAtPosition(pos: number): Type;
getReturnType(): Type;
getDocumentationComment(typeChecker: TypeChecker | undefined): SymbolDisplayPart[];
getJsDocTags(): JSDocTagInfo[];
diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts
index 752f6b892bb..c332bd508eb 100644
--- a/tests/baselines/reference/api/tsserverlibrary.d.ts
+++ b/tests/baselines/reference/api/tsserverlibrary.d.ts
@@ -5560,6 +5560,7 @@ declare namespace ts {
isTypeParameter(): this is TypeParameter;
isClassOrInterface(): this is InterfaceType;
isClass(): this is InterfaceType;
+ isIndexType(): this is IndexType;
}
interface TypeReference {
typeArguments?: readonly Type[];
@@ -5568,6 +5569,7 @@ declare namespace ts {
getDeclaration(): SignatureDeclaration;
getTypeParameters(): TypeParameter[] | undefined;
getParameters(): Symbol[];
+ getTypeParameterAtPosition(pos: number): Type;
getReturnType(): Type;
getDocumentationComment(typeChecker: TypeChecker | undefined): SymbolDisplayPart[];
getJsDocTags(): JSDocTagInfo[];
diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts
index a9d7e9c449b..f17b673f250 100644
--- a/tests/baselines/reference/api/typescript.d.ts
+++ b/tests/baselines/reference/api/typescript.d.ts
@@ -5560,6 +5560,7 @@ declare namespace ts {
isTypeParameter(): this is TypeParameter;
isClassOrInterface(): this is InterfaceType;
isClass(): this is InterfaceType;
+ isIndexType(): this is IndexType;
}
interface TypeReference {
typeArguments?: readonly Type[];
@@ -5568,6 +5569,7 @@ declare namespace ts {
getDeclaration(): SignatureDeclaration;
getTypeParameters(): TypeParameter[] | undefined;
getParameters(): Symbol[];
+ getTypeParameterAtPosition(pos: number): Type;
getReturnType(): Type;
getDocumentationComment(typeChecker: TypeChecker | undefined): SymbolDisplayPart[];
getJsDocTags(): JSDocTagInfo[];
diff --git a/tests/cases/fourslash/completionListAtThisType.ts b/tests/cases/fourslash/completionListAtThisType.ts
new file mode 100644
index 00000000000..3842b6c3cfe
--- /dev/null
+++ b/tests/cases/fourslash/completionListAtThisType.ts
@@ -0,0 +1,19 @@
+///
+
+////class Test {
+//// foo() {}
+////
+//// bar() {
+//// this.baz(this, "/*1*/");
+////
+//// const t = new Test()
+//// this.baz(t, "/*2*/");
+//// }
+////
+//// baz(a: T, k: keyof T) {}
+////}
+
+verify.completions({
+ marker: ["1", "2"],
+ exact: ["foo", "bar", "baz"]
+});