mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-24 01:53:36 -05:00
fix(35954): Change spelling for private field incorrectly fixes to a string property (#36079)
* fix(35954): code fix incorrectly fixes private properties spelling issues * remove duplicate function calls
This commit is contained in:
committed by
Nathan Shively-Sanders
parent
dbd55b3928
commit
eeff036519
@@ -570,9 +570,12 @@ namespace ts {
|
||||
isArrayLikeType,
|
||||
isTypeInvalidDueToUnionDiscriminant,
|
||||
getAllPossiblePropertiesOfTypes,
|
||||
getSuggestionForNonexistentProperty: (node, type) => getSuggestionForNonexistentProperty(node, type),
|
||||
getSuggestedSymbolForNonexistentProperty,
|
||||
getSuggestionForNonexistentProperty,
|
||||
getSuggestedSymbolForNonexistentSymbol: (location, name, meaning) => getSuggestedSymbolForNonexistentSymbol(location, escapeLeadingUnderscores(name), meaning),
|
||||
getSuggestionForNonexistentSymbol: (location, name, meaning) => getSuggestionForNonexistentSymbol(location, escapeLeadingUnderscores(name), meaning),
|
||||
getSuggestionForNonexistentExport: (node, target) => getSuggestionForNonexistentExport(node, target),
|
||||
getSuggestedSymbolForNonexistentModule,
|
||||
getSuggestionForNonexistentExport,
|
||||
getBaseConstraintOfType,
|
||||
getDefaultFromTypeParameter: type => type && type.flags & TypeFlags.TypeParameter ? getDefaultFromTypeParameter(type as TypeParameter) : undefined,
|
||||
resolveName(name, location, meaning, excludeGlobals) {
|
||||
|
||||
@@ -3484,8 +3484,11 @@ namespace ts {
|
||||
*/
|
||||
/* @internal */ tryGetMemberInModuleExportsAndProperties(memberName: string, moduleSymbol: Symbol): Symbol | undefined;
|
||||
getApparentType(type: Type): Type;
|
||||
/* @internal */ getSuggestedSymbolForNonexistentProperty(name: Identifier | PrivateIdentifier | string, containingType: Type): Symbol | undefined;
|
||||
/* @internal */ getSuggestionForNonexistentProperty(name: Identifier | PrivateIdentifier | string, containingType: Type): string | undefined;
|
||||
/* @internal */ getSuggestedSymbolForNonexistentSymbol(location: Node, name: string, meaning: SymbolFlags): Symbol | undefined;
|
||||
/* @internal */ getSuggestionForNonexistentSymbol(location: Node, name: string, meaning: SymbolFlags): string | undefined;
|
||||
/* @internal */ getSuggestedSymbolForNonexistentModule(node: Identifier, target: Symbol): Symbol | undefined;
|
||||
/* @internal */ getSuggestionForNonexistentExport(node: Identifier, target: Symbol): string | undefined;
|
||||
getBaseConstraintOfType(type: Type): Type | undefined;
|
||||
getDefaultFromTypeParameter(type: Type): Type | undefined;
|
||||
|
||||
@@ -14,27 +14,27 @@ namespace ts.codefix {
|
||||
const { sourceFile } = context;
|
||||
const info = getInfo(sourceFile, context.span.start, context);
|
||||
if (!info) return undefined;
|
||||
const { node, suggestion } = info;
|
||||
const { node, suggestedSymbol } = info;
|
||||
const { target } = context.host.getCompilationSettings();
|
||||
const changes = textChanges.ChangeTracker.with(context, t => doChange(t, sourceFile, node, suggestion, target!));
|
||||
return [createCodeFixAction("spelling", changes, [Diagnostics.Change_spelling_to_0, suggestion], fixId, Diagnostics.Fix_all_detected_spelling_errors)];
|
||||
const changes = textChanges.ChangeTracker.with(context, t => doChange(t, sourceFile, node, suggestedSymbol, target!));
|
||||
return [createCodeFixAction("spelling", changes, [Diagnostics.Change_spelling_to_0, symbolName(suggestedSymbol)], fixId, Diagnostics.Fix_all_detected_spelling_errors)];
|
||||
},
|
||||
fixIds: [fixId],
|
||||
getAllCodeActions: context => codeFixAll(context, errorCodes, (changes, diag) => {
|
||||
const info = getInfo(diag.file, diag.start, context);
|
||||
const { target } = context.host.getCompilationSettings();
|
||||
if (info) doChange(changes, context.sourceFile, info.node, info.suggestion, target!);
|
||||
if (info) doChange(changes, context.sourceFile, info.node, info.suggestedSymbol, target!);
|
||||
}),
|
||||
});
|
||||
|
||||
function getInfo(sourceFile: SourceFile, pos: number, context: CodeFixContextBase): { node: Node, suggestion: string } | undefined {
|
||||
function getInfo(sourceFile: SourceFile, pos: number, context: CodeFixContextBase): { node: Node, suggestedSymbol: Symbol } | undefined {
|
||||
// This is the identifier of the misspelled word. eg:
|
||||
// this.speling = 1;
|
||||
// ^^^^^^^
|
||||
const node = getTokenAtPosition(sourceFile, pos);
|
||||
const checker = context.program.getTypeChecker();
|
||||
|
||||
let suggestion: string | undefined;
|
||||
let suggestedSymbol: Symbol | undefined;
|
||||
if (isPropertyAccessExpression(node.parent) && node.parent.name === node) {
|
||||
Debug.assert(isIdentifierOrPrivateIdentifier(node), "Expected an identifier for spelling (property access)");
|
||||
let containingType = checker.getTypeAtLocation(node.parent.expression);
|
||||
@@ -42,29 +42,36 @@ namespace ts.codefix {
|
||||
containingType = checker.getNonNullableType(containingType);
|
||||
}
|
||||
const name = node as Identifier | PrivateIdentifier;
|
||||
suggestion = checker.getSuggestionForNonexistentProperty(name, containingType);
|
||||
suggestedSymbol = checker.getSuggestedSymbolForNonexistentProperty(name, containingType);
|
||||
}
|
||||
else if (isImportSpecifier(node.parent) && node.parent.name === node) {
|
||||
Debug.assert(node.kind === SyntaxKind.Identifier, "Expected an identifier for spelling (import)");
|
||||
const importDeclaration = findAncestor(node, isImportDeclaration)!;
|
||||
const resolvedSourceFile = getResolvedSourceFileFromImportDeclaration(sourceFile, context, importDeclaration);
|
||||
if (resolvedSourceFile && resolvedSourceFile.symbol) {
|
||||
suggestion = checker.getSuggestionForNonexistentExport(node as Identifier, resolvedSourceFile.symbol);
|
||||
suggestedSymbol = checker.getSuggestedSymbolForNonexistentModule(node as Identifier, resolvedSourceFile.symbol);
|
||||
}
|
||||
}
|
||||
else {
|
||||
const meaning = getMeaningFromLocation(node);
|
||||
const name = getTextOfNode(node);
|
||||
Debug.assert(name !== undefined, "name should be defined");
|
||||
suggestion = checker.getSuggestionForNonexistentSymbol(node, name, convertSemanticMeaningToSymbolFlags(meaning));
|
||||
suggestedSymbol = checker.getSuggestedSymbolForNonexistentSymbol(node, name, convertSemanticMeaningToSymbolFlags(meaning));
|
||||
}
|
||||
|
||||
return suggestion === undefined ? undefined : { node, suggestion };
|
||||
return suggestedSymbol === undefined ? undefined : { node, suggestedSymbol };
|
||||
}
|
||||
|
||||
function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, node: Node, suggestion: string, target: ScriptTarget) {
|
||||
function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, node: Node, suggestedSymbol: Symbol, target: ScriptTarget) {
|
||||
const suggestion = symbolName(suggestedSymbol);
|
||||
if (!isIdentifierText(suggestion, target) && isPropertyAccessExpression(node.parent)) {
|
||||
changes.replaceNode(sourceFile, node.parent, createElementAccess(node.parent.expression, createLiteral(suggestion)));
|
||||
const valDecl = suggestedSymbol.valueDeclaration;
|
||||
if (isNamedDeclaration(valDecl) && isPrivateIdentifier(valDecl.name)) {
|
||||
changes.replaceNode(sourceFile, node, createIdentifier(suggestion));
|
||||
}
|
||||
else {
|
||||
changes.replaceNode(sourceFile, node.parent, createElementAccess(node.parent.expression, createLiteral(suggestion)));
|
||||
}
|
||||
}
|
||||
else {
|
||||
changes.replaceNode(sourceFile, node, createIdentifier(suggestion));
|
||||
|
||||
21
tests/cases/fourslash/codeFixSpellingPrivatePropertyName.ts
Normal file
21
tests/cases/fourslash/codeFixSpellingPrivatePropertyName.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////class A {
|
||||
//// #foo: number;
|
||||
//// constructor() {
|
||||
//// [|this.foo = 1;|]
|
||||
//// }
|
||||
////}
|
||||
|
||||
verify.codeFixAvailable([
|
||||
{ description: "Change spelling to '#foo'" },
|
||||
{ description: "Declare property 'foo'" },
|
||||
{ description: "Add index signature for property 'foo'" },
|
||||
{ description: "Remove declaration for: '#foo'" },
|
||||
]);
|
||||
|
||||
verify.codeFix({
|
||||
index: 0,
|
||||
description: "Change spelling to '#foo'",
|
||||
newRangeContent: "this.#foo = 1;"
|
||||
});
|
||||
@@ -0,0 +1,20 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////class A {
|
||||
//// "#foo": number = 100;
|
||||
//// constructor() {
|
||||
//// [|this.foo = 1;|]
|
||||
//// }
|
||||
////}
|
||||
|
||||
verify.codeFixAvailable([
|
||||
{ description: "Change spelling to '#foo'" },
|
||||
{ description: "Declare property 'foo'" },
|
||||
{ description: "Add index signature for property 'foo'" }
|
||||
]);
|
||||
|
||||
verify.codeFix({
|
||||
index: 0,
|
||||
description: "Change spelling to '#foo'",
|
||||
newRangeContent: 'this["#foo"] = 1;'
|
||||
});
|
||||
Reference in New Issue
Block a user