Skip to content

Commit e9fd831

Browse files
authored
Merge pull request microsoft#14140 from aozgaa/ImplementMissingThis
Implement Missing Property of Type `this`
2 parents b131723 + 0261586 commit e9fd831

11 files changed

+78
-36
lines changed

src/compiler/checker.ts

+13-2
Original file line numberDiff line numberDiff line change
@@ -21144,7 +21144,15 @@ namespace ts {
2114421144
}
2114521145

2114621146
if (isPartOfTypeNode(node)) {
21147-
return getTypeFromTypeNode(<TypeNode>node);
21147+
let typeFromTypeNode = getTypeFromTypeNode(<TypeNode>node);
21148+
21149+
if (typeFromTypeNode && isExpressionWithTypeArgumentsInClassImplementsClause(node)) {
21150+
const containingClass = getContainingClass(node);
21151+
const classType = getTypeOfNode(containingClass) as InterfaceType;
21152+
typeFromTypeNode = getTypeWithThisArgument(typeFromTypeNode, classType.thisType);
21153+
}
21154+
21155+
return typeFromTypeNode;
2114821156
}
2114921157

2115021158
if (isPartOfExpression(node)) {
@@ -21154,7 +21162,10 @@ namespace ts {
2115421162
if (isExpressionWithTypeArgumentsInClassExtendsClause(node)) {
2115521163
// A SyntaxKind.ExpressionWithTypeArguments is considered a type node, except when it occurs in the
2115621164
// extends clause of a class. We handle that case here.
21157-
return getBaseTypes(<InterfaceType>getDeclaredTypeOfSymbol(getSymbolOfNode(node.parent.parent)))[0];
21165+
const classNode = getContainingClass(node);
21166+
const classType = getDeclaredTypeOfSymbol(getSymbolOfNode(classNode)) as InterfaceType;
21167+
const baseType = getBaseTypes(classType)[0];
21168+
return baseType && getTypeWithThisArgument(baseType, classType.thisType);
2115821169
}
2115921170

2116021171
if (isTypeDeclaration(node)) {

src/compiler/utilities.ts

+9
Original file line numberDiff line numberDiff line change
@@ -3129,6 +3129,15 @@ namespace ts {
31293129
return tryGetClassExtendingExpressionWithTypeArguments(node) !== undefined;
31303130
}
31313131

3132+
export function isExpressionWithTypeArgumentsInClassImplementsClause(node: Node): node is ExpressionWithTypeArguments {
3133+
return node.kind === SyntaxKind.ExpressionWithTypeArguments
3134+
&& isEntityNameExpression((node as ExpressionWithTypeArguments).expression)
3135+
&& node.parent
3136+
&& (<HeritageClause>node.parent).token === SyntaxKind.ImplementsKeyword
3137+
&& node.parent.parent
3138+
&& isClassLike(node.parent.parent);
3139+
}
3140+
31323141
export function isEntityNameExpression(node: Expression): node is EntityNameExpression {
31333142
return node.kind === SyntaxKind.Identifier ||
31343143
node.kind === SyntaxKind.PropertyAccessExpression && isEntityNameExpression((<PropertyAccessExpression>node).expression);

src/services/codefixes/fixClassDoesntImplementInheritedAbstractMember.ts

+2-2
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

+2-2
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,17 @@ 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;
2928
// Note that this is ultimately derived from a map indexed by symbol names,
3029
// so duplicates cannot occur.
30+
const implementedType = checker.getTypeAtLocation(implementedTypeNode) as InterfaceType;
3131
const implementedTypeSymbols = checker.getPropertiesOfType(implementedType);
3232
const nonPrivateMembers = implementedTypeSymbols.filter(symbol => !(getModifierFlags(symbol.valueDeclaration) & ModifierFlags.Private));
3333

src/services/codefixes/helpers.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,6 @@ namespace ts.codefix {
2323
* @returns Empty string iff there we can't figure out a representation for `symbol` in `enclosingDeclaration`.
2424
*/
2525
function getInsertionForMemberSymbol(symbol: Symbol, enclosingDeclaration: ClassLikeDeclaration, checker: TypeChecker, newlineChar: string): string {
26-
// const name = symbol.getName();
27-
const type = checker.getTypeOfSymbolAtLocation(symbol, enclosingDeclaration);
2826
const declarations = symbol.getDeclarations();
2927
if (!(declarations && declarations.length)) {
3028
return "";
@@ -34,6 +32,8 @@ namespace ts.codefix {
3432
const name = declaration.name ? declaration.name.getText() : undefined;
3533
const visibility = getVisibilityPrefixWithSpace(getModifierFlags(declaration));
3634

35+
const type = checker.getTypeOfSymbolAtLocation(symbol, enclosingDeclaration);
36+
3737
switch (declaration.kind) {
3838
case SyntaxKind.GetAccessor:
3939
case SyntaxKind.SetAccessor:

tests/cases/fourslash/codeFixClassExtendAbstractGetterSetter.ts

+18-4
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,17 @@
22

33
//// abstract class A {
44
//// private _a: string;
5-
////
6-
//// abstract get a(): string;
7-
//// abstract set a(newName: string);
5+
////
6+
//// abstract get a(): number | string;
7+
//// abstract get b(): this;
8+
//// abstract get c(): A;
9+
////
10+
//// abstract set d(arg: number | string);
11+
//// abstract set e(arg: this);
12+
//// abstract set f(arg: A);
13+
////
14+
//// abstract get g(): string;
15+
//// abstract set g(newName: string);
816
//// }
917
////
1018
//// // Don't need to add anything in this case.
@@ -13,5 +21,11 @@
1321
//// class C extends A {[| |]}
1422

1523
verify.rangeAfterCodeFix(`
16-
a: string;
24+
a: string | number;
25+
b: this;
26+
c: A;
27+
d: string | number;
28+
e: this;
29+
f: A;
30+
g: string;
1731
`);

tests/cases/fourslash/codeFixClassExtendAbstractMethod.ts

+2
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) {
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1-
/// <reference path='fourslash.ts' />
2-
3-
//// abstract class A {
4-
//// abstract set c(arg: number | string);
5-
//// }
6-
////
7-
//// class C extends A {[| |]}
8-
9-
verify.rangeAfterCodeFix(`
10-
c: string | number;
11-
`);
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+
`);

tests/cases/fourslash/codeFixClassExtendAbstractProperty.ts

+4
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
//// abstract class A {
44
//// abstract x: number;
5+
//// abstract y: this;
6+
//// abstract z: A;
57
//// abstract foo(): number;
68
//// }
79
////
@@ -10,6 +12,8 @@
1012

1113
verify.rangeAfterCodeFix(`
1214
x: number;
15+
y: this;
16+
z: A;
1317
foo(): number {
1418
throw new Error('Method not implemented.');
1519
}
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
/// <reference path='fourslash.ts' />
2-
3-
//// abstract class A {
4-
//// abstract get b(): number;
5-
//// }
6-
////
7-
//// class C extends A {[| |]}
8-
9-
verify.rangeAfterCodeFix(`
10-
b: number;
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)