Merge pull request #14140 from aozgaa/ImplementMissingThis

Implement Missing Property of Type `this`
This commit is contained in:
Arthur Ozga
2017-03-01 16:17:35 -08:00
committed by GitHub
11 changed files with 78 additions and 36 deletions

View File

@@ -21144,7 +21144,15 @@ namespace ts {
}
if (isPartOfTypeNode(node)) {
return getTypeFromTypeNode(<TypeNode>node);
let typeFromTypeNode = getTypeFromTypeNode(<TypeNode>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(<InterfaceType>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)) {

View File

@@ -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
&& (<HeritageClause>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((<PropertyAccessExpression>node).expression);

View File

@@ -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.

View File

@@ -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));

View File

@@ -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:

View File

@@ -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;
`);

View File

@@ -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) {

View File

@@ -1,11 +1,13 @@
/// <reference path='fourslash.ts' />
//// abstract class A {
//// abstract set c(arg: number | string);
//// }
////
//// class C extends A {[| |]}
verify.rangeAfterCodeFix(`
c: string | number;
`);
/// <reference path='fourslash.ts' />
//// abstract class A {
//// abstract f(): this;
//// }
////
//// class C extends A {[| |]}
verify.rangeAfterCodeFix(`
f(): this {
throw new Error('Method not implemented.');
}
`);

View File

@@ -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.');
}

View File

@@ -1,11 +1,11 @@
/// <reference path='fourslash.ts' />
//// abstract class A {
//// abstract get b(): number;
//// }
////
//// class C extends A {[| |]}
verify.rangeAfterCodeFix(`
b: number;
`);
/// <reference path='fourslash.ts' />
//// abstract class A {
//// abstract x: this;
//// }
////
//// class C extends A {[| |]}
verify.rangeAfterCodeFix(`
x: this;
`);

View File

@@ -1,14 +1,14 @@
/// <reference path='fourslash.ts' />
//// 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.');
}
`);