diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3104114e7b7..9cd5b7a31d2 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -21144,7 +21144,15 @@ namespace ts { } if (isPartOfTypeNode(node)) { - return getTypeFromTypeNode(node); + let typeFromTypeNode = getTypeFromTypeNode(node); + + if (typeFromTypeNode && isExpressionWithTypeArgumentsInClassImplementsClause(node)) { + const containingClass = getContainingClass(node); + const classType = getTypeOfNode(containingClass) as InterfaceType; + typeFromTypeNode = getTypeWithThisArgument(typeFromTypeNode, classType.thisType); + } + + return typeFromTypeNode; } if (isPartOfExpression(node)) { @@ -21154,7 +21162,10 @@ namespace ts { if (isExpressionWithTypeArgumentsInClassExtendsClause(node)) { // A SyntaxKind.ExpressionWithTypeArguments is considered a type node, except when it occurs in the // extends clause of a class. We handle that case here. - return getBaseTypes(getDeclaredTypeOfSymbol(getSymbolOfNode(node.parent.parent)))[0]; + const classNode = getContainingClass(node); + const classType = getDeclaredTypeOfSymbol(getSymbolOfNode(classNode)) as InterfaceType; + const baseType = getBaseTypes(classType)[0]; + return baseType && getTypeWithThisArgument(baseType, classType.thisType); } if (isTypeDeclaration(node)) { diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 6d69d54b113..e107c4b669b 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -3129,6 +3129,15 @@ namespace ts { return tryGetClassExtendingExpressionWithTypeArguments(node) !== undefined; } + export function isExpressionWithTypeArgumentsInClassImplementsClause(node: Node): node is ExpressionWithTypeArguments { + return node.kind === SyntaxKind.ExpressionWithTypeArguments + && isEntityNameExpression((node as ExpressionWithTypeArguments).expression) + && node.parent + && (node.parent).token === SyntaxKind.ImplementsKeyword + && node.parent.parent + && isClassLike(node.parent.parent); + } + export function isEntityNameExpression(node: Expression): node is EntityNameExpression { return node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.PropertyAccessExpression && isEntityNameExpression((node).expression); diff --git a/src/services/codefixes/fixClassDoesntImplementInheritedAbstractMember.ts b/src/services/codefixes/fixClassDoesntImplementInheritedAbstractMember.ts index 22546e55ecc..16edce0a516 100644 --- a/src/services/codefixes/fixClassDoesntImplementInheritedAbstractMember.ts +++ b/src/services/codefixes/fixClassDoesntImplementInheritedAbstractMember.ts @@ -22,8 +22,8 @@ namespace ts.codefix { const classDecl = token.parent as ClassLikeDeclaration; const startPos = classDecl.members.pos; - const classType = checker.getTypeAtLocation(classDecl) as InterfaceType; - const instantiatedExtendsType = checker.getBaseTypes(classType)[0]; + const extendsNode = getClassExtendsHeritageClauseElement(classDecl); + const instantiatedExtendsType = checker.getTypeAtLocation(extendsNode); // Note that this is ultimately derived from a map indexed by symbol names, // so duplicates cannot occur. diff --git a/src/services/codefixes/fixClassIncorrectlyImplementsInterface.ts b/src/services/codefixes/fixClassIncorrectlyImplementsInterface.ts index 42488dc2aed..67b2242c8d9 100644 --- a/src/services/codefixes/fixClassIncorrectlyImplementsInterface.ts +++ b/src/services/codefixes/fixClassIncorrectlyImplementsInterface.ts @@ -17,7 +17,7 @@ namespace ts.codefix { } const startPos: number = classDecl.members.pos; - const classType = checker.getTypeAtLocation(classDecl); + const classType = checker.getTypeAtLocation(classDecl) as InterfaceType; const implementedTypeNodes = getClassImplementsHeritageClauseElements(classDecl); const hasNumericIndexSignature = !!checker.getIndexTypeOfType(classType, IndexKind.Number); @@ -25,9 +25,9 @@ namespace ts.codefix { const result: CodeAction[] = []; for (const implementedTypeNode of implementedTypeNodes) { - const implementedType = checker.getTypeFromTypeNode(implementedTypeNode) as InterfaceType; // Note that this is ultimately derived from a map indexed by symbol names, // so duplicates cannot occur. + const implementedType = checker.getTypeAtLocation(implementedTypeNode) as InterfaceType; const implementedTypeSymbols = checker.getPropertiesOfType(implementedType); const nonPrivateMembers = implementedTypeSymbols.filter(symbol => !(getModifierFlags(symbol.valueDeclaration) & ModifierFlags.Private)); diff --git a/src/services/codefixes/helpers.ts b/src/services/codefixes/helpers.ts index 3eab994f84c..d20fc0129cd 100644 --- a/src/services/codefixes/helpers.ts +++ b/src/services/codefixes/helpers.ts @@ -23,8 +23,6 @@ namespace ts.codefix { * @returns Empty string iff there we can't figure out a representation for `symbol` in `enclosingDeclaration`. */ function getInsertionForMemberSymbol(symbol: Symbol, enclosingDeclaration: ClassLikeDeclaration, checker: TypeChecker, newlineChar: string): string { - // const name = symbol.getName(); - const type = checker.getTypeOfSymbolAtLocation(symbol, enclosingDeclaration); const declarations = symbol.getDeclarations(); if (!(declarations && declarations.length)) { return ""; @@ -34,6 +32,8 @@ namespace ts.codefix { const name = declaration.name ? declaration.name.getText() : undefined; const visibility = getVisibilityPrefixWithSpace(getModifierFlags(declaration)); + const type = checker.getTypeOfSymbolAtLocation(symbol, enclosingDeclaration); + switch (declaration.kind) { case SyntaxKind.GetAccessor: case SyntaxKind.SetAccessor: diff --git a/tests/cases/fourslash/codeFixClassExtendAbstractGetterSetter.ts b/tests/cases/fourslash/codeFixClassExtendAbstractGetterSetter.ts index fc0ac400623..4bddfb799f2 100644 --- a/tests/cases/fourslash/codeFixClassExtendAbstractGetterSetter.ts +++ b/tests/cases/fourslash/codeFixClassExtendAbstractGetterSetter.ts @@ -2,9 +2,17 @@ //// abstract class A { //// private _a: string; -//// -//// abstract get a(): string; -//// abstract set a(newName: string); +//// +//// abstract get a(): number | string; +//// abstract get b(): this; +//// abstract get c(): A; +//// +//// abstract set d(arg: number | string); +//// abstract set e(arg: this); +//// abstract set f(arg: A); +//// +//// abstract get g(): string; +//// abstract set g(newName: string); //// } //// //// // Don't need to add anything in this case. @@ -13,5 +21,11 @@ //// class C extends A {[| |]} verify.rangeAfterCodeFix(` - a: string; + a: string | number; + b: this; + c: A; + d: string | number; + e: this; + f: A; + g: string; `); \ No newline at end of file diff --git a/tests/cases/fourslash/codeFixClassExtendAbstractMethod.ts b/tests/cases/fourslash/codeFixClassExtendAbstractMethod.ts index a657dfeb718..344a7ee3f2b 100644 --- a/tests/cases/fourslash/codeFixClassExtendAbstractMethod.ts +++ b/tests/cases/fourslash/codeFixClassExtendAbstractMethod.ts @@ -2,6 +2,7 @@ //// abstract class A { //// abstract f(a: number, b: string): boolean; +//// abstract f(a: number, b: string): this; //// abstract f(a: string, b: number): Function; //// abstract f(a: string): Function; //// } @@ -10,6 +11,7 @@ verify.rangeAfterCodeFix(` f(a: number, b: string): boolean; + f(a: number, b: string): this; f(a: string, b: number): Function; f(a: string): Function; f(a: any, b?: any) { diff --git a/tests/cases/fourslash/codeFixClassExtendAbstractSetter.ts b/tests/cases/fourslash/codeFixClassExtendAbstractMethodThis.ts similarity index 50% rename from tests/cases/fourslash/codeFixClassExtendAbstractSetter.ts rename to tests/cases/fourslash/codeFixClassExtendAbstractMethodThis.ts index e8cb55fa660..55b3ad4b77e 100644 --- a/tests/cases/fourslash/codeFixClassExtendAbstractSetter.ts +++ b/tests/cases/fourslash/codeFixClassExtendAbstractMethodThis.ts @@ -1,11 +1,13 @@ -/// - -//// abstract class A { -//// abstract set c(arg: number | string); -//// } -//// -//// class C extends A {[| |]} - -verify.rangeAfterCodeFix(` - c: string | number; -`); \ No newline at end of file +/// + +//// abstract class A { +//// abstract f(): this; +//// } +//// +//// class C extends A {[| |]} + +verify.rangeAfterCodeFix(` + f(): this { + throw new Error('Method not implemented.'); + } +`); diff --git a/tests/cases/fourslash/codeFixClassExtendAbstractProperty.ts b/tests/cases/fourslash/codeFixClassExtendAbstractProperty.ts index 3160a3b9a08..b7300acf5ae 100644 --- a/tests/cases/fourslash/codeFixClassExtendAbstractProperty.ts +++ b/tests/cases/fourslash/codeFixClassExtendAbstractProperty.ts @@ -2,6 +2,8 @@ //// abstract class A { //// abstract x: number; +//// abstract y: this; +//// abstract z: A; //// abstract foo(): number; //// } //// @@ -10,6 +12,8 @@ verify.rangeAfterCodeFix(` x: number; + y: this; + z: A; foo(): number { throw new Error('Method not implemented.'); } diff --git a/tests/cases/fourslash/codeFixClassExtendAbstractGetter.ts b/tests/cases/fourslash/codeFixClassExtendAbstractPropertyThis.ts similarity index 68% rename from tests/cases/fourslash/codeFixClassExtendAbstractGetter.ts rename to tests/cases/fourslash/codeFixClassExtendAbstractPropertyThis.ts index 8d79cce710e..de128ca1b79 100644 --- a/tests/cases/fourslash/codeFixClassExtendAbstractGetter.ts +++ b/tests/cases/fourslash/codeFixClassExtendAbstractPropertyThis.ts @@ -1,11 +1,11 @@ -/// - -//// abstract class A { -//// abstract get b(): number; -//// } -//// -//// class C extends A {[| |]} - -verify.rangeAfterCodeFix(` - b: number; -`); \ No newline at end of file +/// + +//// abstract class A { +//// abstract x: this; +//// } +//// +//// class C extends A {[| |]} + +verify.rangeAfterCodeFix(` + x: this; +`); diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceMethodWithParams.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceMethodThisAndSelfReference.ts similarity index 71% rename from tests/cases/fourslash/codeFixClassImplementInterfaceMethodWithParams.ts rename to tests/cases/fourslash/codeFixClassImplementInterfaceMethodThisAndSelfReference.ts index 0ee842975e0..95cc3476bf1 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceMethodWithParams.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceMethodThisAndSelfReference.ts @@ -1,14 +1,14 @@ /// //// interface I { -//// f(x: number, y: string): I +//// f(x: number, y: this): I //// } //// //// class C implements I {[| //// |]} verify.rangeAfterCodeFix(` -f(x: number,y: string): I { +f(x: number,y: this): I { throw new Error('Method not implemented.'); } `);