fixAddMissingMember: Remove special-casing for 'this' (#22987)

* fixAddMissingMember: Remove special-casing for 'this'

* Update type of 'call'
This commit is contained in:
Andy
2018-03-29 12:54:17 -07:00
committed by GitHub
parent 51a4fe6d20
commit d5a7dc1053
3 changed files with 21 additions and 41 deletions

View File

@@ -46,7 +46,7 @@ namespace ts.codefix {
},
});
interface Info { token: Identifier; classDeclaration: ClassLikeDeclaration; makeStatic: boolean; classDeclarationSourceFile: SourceFile; inJs: boolean; call: CallExpression; }
interface Info { token: Identifier; classDeclaration: ClassLikeDeclaration; makeStatic: boolean; classDeclarationSourceFile: SourceFile; inJs: boolean; call: CallExpression | undefined; }
function getInfo(tokenSourceFile: SourceFile, tokenPos: number, checker: TypeChecker): Info | undefined {
// The identifier of the missing property. eg:
// this.missing = 1;
@@ -56,45 +56,22 @@ namespace ts.codefix {
return undefined;
}
const classAndMakeStatic = getClassAndMakeStatic(token, checker);
if (!classAndMakeStatic) {
return undefined;
}
const { classDeclaration, makeStatic } = classAndMakeStatic;
const { parent } = token;
if (!isPropertyAccessExpression(parent)) return undefined;
const leftExpressionType = skipConstraint(checker.getTypeAtLocation(parent.expression));
const { symbol } = leftExpressionType;
const classDeclaration = symbol && symbol.declarations && find(symbol.declarations, isClassLike);
if (!classDeclaration) return undefined;
const makeStatic = (leftExpressionType as TypeReference).target !== checker.getDeclaredTypeOfSymbol(symbol);
const classDeclarationSourceFile = classDeclaration.getSourceFile();
const inJs = isInJavaScriptFile(classDeclarationSourceFile);
const call = tryCast(token.parent.parent, isCallExpression);
const inJs = isSourceFileJavaScript(classDeclarationSourceFile);
const call = tryCast(parent.parent, isCallExpression);
return { token, classDeclaration, makeStatic, classDeclarationSourceFile, inJs, call };
}
function getClassAndMakeStatic(token: Node, checker: TypeChecker): { readonly classDeclaration: ClassLikeDeclaration, readonly makeStatic: boolean } | undefined {
const { parent } = token;
if (!isPropertyAccessExpression(parent)) {
return undefined;
}
if (parent.expression.kind === SyntaxKind.ThisKeyword) {
const containingClassMemberDeclaration = getThisContainer(token, /*includeArrowFunctions*/ false);
if (!isClassElement(containingClassMemberDeclaration)) {
return undefined;
}
const classDeclaration = containingClassMemberDeclaration.parent;
// Property accesses on `this` in a static method are accesses of a static member.
return isClassLike(classDeclaration) ? { classDeclaration, makeStatic: hasModifier(containingClassMemberDeclaration, ModifierFlags.Static) } : undefined;
}
else {
const leftExpressionType = checker.getTypeAtLocation(parent.expression);
const { symbol } = leftExpressionType;
if (!(symbol && leftExpressionType.flags & TypeFlags.Object && symbol.flags & SymbolFlags.Class)) {
return undefined;
}
const classDeclaration = cast(first(symbol.declarations), isClassLike);
// The expression is a class symbol but the type is not the instance-side.
return { classDeclaration, makeStatic: leftExpressionType !== checker.getDeclaredTypeOfSymbol(symbol) };
}
}
function getActionsForAddMissingMemberInJavaScriptFile(context: CodeFixContext, classDeclarationSourceFile: SourceFile, classDeclaration: ClassLikeDeclaration, tokenName: string, makeStatic: boolean): CodeFixAction | undefined {
const changes = textChanges.ChangeTracker.with(context, t => addMissingMemberInJs(t, classDeclarationSourceFile, classDeclaration, tokenName, makeStatic));
return changes.length === 0 ? undefined

View File

@@ -462,13 +462,12 @@ namespace ts.Completions {
return type && { kind: StringLiteralCompletionKind.Properties, symbols: type.getApparentProperties(), hasIndexSignature: hasIndexSignature(type) };
}
function getStringLiteralTypes(type: Type, typeChecker: TypeChecker, uniques = createMap<true>()): ReadonlyArray<StringLiteralType> {
if (type && type.flags & TypeFlags.TypeParameter) {
type = type.getConstraint();
}
return type && type.flags & TypeFlags.Union
function getStringLiteralTypes(type: Type | undefined, typeChecker: TypeChecker, uniques = createMap<true>()): ReadonlyArray<StringLiteralType> | undefined {
if (!type) return emptyArray;
type = skipConstraint(type);
return type.flags & TypeFlags.Union
? flatMap((<UnionType>type).types, t => getStringLiteralTypes(t, typeChecker, uniques))
: type && type.flags & TypeFlags.StringLiteral && !(type.flags & TypeFlags.EnumLiteral) && addToSeen(uniques, (type as StringLiteralType).value)
: type.flags & TypeFlags.StringLiteral && !(type.flags & TypeFlags.EnumLiteral) && addToSeen(uniques, (type as StringLiteralType).value)
? [type as StringLiteralType]
: emptyArray;
}

View File

@@ -1216,6 +1216,10 @@ namespace ts {
}
return result;
}
export function skipConstraint(type: Type): Type {
return type.flags & TypeFlags.TypeParameter ? type.getConstraint() : type;
}
}
// Display-part writer helpers