Skip to content

Commit 5bcbd7a

Browse files
author
Arthur Ozga
committed
instantiate generic this param correctly
1 parent c77ea9e commit 5bcbd7a

9 files changed

+56
-19
lines changed

src/compiler/checker.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21106,7 +21106,15 @@ namespace ts {
2110621106
}
2110721107

2110821108
if (isPartOfTypeNode(node)) {
21109-
return getTypeFromTypeNode(<TypeNode>node);
21109+
let typeFromTypeNode = getTypeFromTypeNode(<TypeNode>node);
21110+
21111+
if (typeFromTypeNode && isExpressionWithTypeArgumentsInClassImplementsClause(node)) {
21112+
const containingClass = getContainingClass(node);
21113+
const classType = getTypeOfNode(containingClass) as InterfaceType;
21114+
typeFromTypeNode = getTypeWithThisArgument(typeFromTypeNode, classType.thisType);
21115+
}
21116+
21117+
return typeFromTypeNode;
2111021118
}
2111121119

2111221120
if (isPartOfExpression(node)) {
@@ -21116,7 +21124,10 @@ namespace ts {
2111621124
if (isExpressionWithTypeArgumentsInClassExtendsClause(node)) {
2111721125
// A SyntaxKind.ExpressionWithTypeArguments is considered a type node, except when it occurs in the
2111821126
// extends clause of a class. We handle that case here.
21119-
return getBaseTypes(<InterfaceType>getDeclaredTypeOfSymbol(getSymbolOfNode(node.parent.parent)))[0];
21127+
const classNode = getContainingClass(node);
21128+
const classType = getDeclaredTypeOfSymbol(getSymbolOfNode(classNode)) as InterfaceType; classType;
21129+
const baseType = getBaseTypes(classType)[0]; baseType;
21130+
return baseType && getTypeWithThisArgument(baseType, classType.thisType);
2112021131
}
2112121132

2112221133
if (isTypeDeclaration(node)) {

src/compiler/utilities.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3126,6 +3126,14 @@ namespace ts {
31263126
return tryGetClassExtendingExpressionWithTypeArguments(node) !== undefined;
31273127
}
31283128

3129+
export function isExpressionWithTypeArgumentsInClassImplementsClause(node: Node): boolean {
3130+
return node.kind === SyntaxKind.ExpressionWithTypeArguments
3131+
&& node.parent
3132+
&& (<HeritageClause>node.parent).token === SyntaxKind.ImplementsKeyword
3133+
&& node.parent.parent
3134+
&& isClassLike(node.parent.parent);
3135+
}
3136+
31293137
export function isEntityNameExpression(node: Expression): node is EntityNameExpression {
31303138
return node.kind === SyntaxKind.Identifier ||
31313139
node.kind === SyntaxKind.PropertyAccessExpression && isEntityNameExpression((<PropertyAccessExpression>node).expression);

src/services/codefixes/fixClassDoesntImplementInheritedAbstractMember.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ namespace ts.codefix {
2222
const classDecl = token.parent as ClassLikeDeclaration;
2323
const startPos = classDecl.members.pos;
2424

25-
const classType = checker.getTypeAtLocation(classDecl) as InterfaceType;
26-
const instantiatedExtendsType = checker.getBaseTypes(classType)[0];
25+
const extendsNode = getClassExtendsHeritageClauseElement(classDecl);
26+
const instantiatedExtendsType = checker.getTypeAtLocation(extendsNode);
2727

2828
// Note that this is ultimately derived from a map indexed by symbol names,
2929
// so duplicates cannot occur.

src/services/codefixes/fixClassIncorrectlyImplementsInterface.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,15 @@ namespace ts.codefix {
1717
}
1818

1919
const startPos: number = classDecl.members.pos;
20-
const classType = checker.getTypeAtLocation(classDecl);
20+
const classType = checker.getTypeAtLocation(classDecl) as InterfaceType;
2121
const implementedTypeNodes = getClassImplementsHeritageClauseElements(classDecl);
2222

2323
const hasNumericIndexSignature = !!checker.getIndexTypeOfType(classType, IndexKind.Number);
2424
const hasStringIndexSignature = !!checker.getIndexTypeOfType(classType, IndexKind.String);
2525

2626
const result: CodeAction[] = [];
2727
for (const implementedTypeNode of implementedTypeNodes) {
28-
const implementedType = checker.getTypeFromTypeNode(implementedTypeNode) as InterfaceType;
28+
const implementedType = checker.getTypeAtLocation(implementedTypeNode) as InterfaceType;
2929
// Note that this is ultimately derived from a map indexed by symbol names,
3030
// so duplicates cannot occur.
3131
const implementedTypeSymbols = checker.getPropertiesOfType(implementedType);

src/services/codefixes/helpers.ts

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -32,22 +32,14 @@ namespace ts.codefix {
3232
const name = declaration.name ? declaration.name.getText() : undefined;
3333
const visibility = getVisibilityPrefixWithSpace(getModifierFlags(declaration));
3434

35-
const typeAtNewDeclaration = checker.getTypeOfSymbolAtLocation(symbol, enclosingDeclaration);
35+
const type = checker.getTypeOfSymbolAtLocation(symbol, enclosingDeclaration);
3636

3737
switch (declaration.kind) {
3838
case SyntaxKind.GetAccessor:
3939
case SyntaxKind.SetAccessor:
4040
case SyntaxKind.PropertySignature:
4141
case SyntaxKind.PropertyDeclaration:
42-
let typeString: string | undefined = undefined;
43-
const typeAtOldDeclaration = checker.getTypeAtLocation(declaration);
44-
if ((typeAtOldDeclaration as TypeParameter).isThisType) {
45-
typeString = "this";
46-
}
47-
else {
48-
typeString = checker.typeToString(typeAtNewDeclaration, enclosingDeclaration, TypeFormatFlags.None);
49-
}
50-
42+
const typeString = checker.typeToString(type, enclosingDeclaration, TypeFormatFlags.None);
5143
return `${visibility}${name}: ${typeString};${newlineChar}`;
5244

5345
case SyntaxKind.MethodSignature:
@@ -59,7 +51,7 @@ namespace ts.codefix {
5951
// If there is more than one overload but no implementation signature
6052
// (eg: an abstract method or interface declaration), there is a 1-1
6153
// correspondence of declarations and signatures.
62-
const signatures = checker.getSignaturesOfType(typeAtNewDeclaration, SignatureKind.Call);
54+
const signatures = checker.getSignaturesOfType(type, SignatureKind.Call);
6355
if (!(signatures && signatures.length > 0)) {
6456
return "";
6557
}

tests/cases/fourslash/codeFixClassExtendAbstractMethod.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
//// abstract class A {
44
//// abstract f(a: number, b: string): boolean;
5+
//// abstract f(a: number, b: string): this;
56
//// abstract f(a: string, b: number): Function;
67
//// abstract f(a: string): Function;
78
//// }
@@ -10,6 +11,7 @@
1011

1112
verify.rangeAfterCodeFix(`
1213
f(a: number, b: string): boolean;
14+
f(a: number, b: string): this;
1315
f(a: string, b: number): Function;
1416
f(a: string): Function;
1517
f(a: any, b?: any) {
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
//// abstract class A {
4+
//// abstract f(): this;
5+
//// }
6+
////
7+
//// class C extends A {[| |]}
8+
9+
verify.rangeAfterCodeFix(`
10+
f(): this {
11+
throw new Error('Method not implemented.');
12+
}
13+
`);
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
//// abstract class A {
4+
//// abstract x: this;
5+
//// }
6+
////
7+
//// class C extends A {[| |]}
8+
9+
verify.rangeAfterCodeFix(`
10+
x: this;
11+
`);
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
/// <reference path='fourslash.ts' />
22

33
//// interface I {
4-
//// f(x: number, y: string): I
4+
//// f(x: number, y: this): I
55
//// }
66
////
77
//// class C implements I {[|
88
//// |]}
99

1010
verify.rangeAfterCodeFix(`
11-
f(x: number,y: string): I {
11+
f(x: number,y: this): I {
1212
throw new Error('Method not implemented.');
1313
}
1414
`);

0 commit comments

Comments
 (0)