Skip to content

TypeScript should infer newLocal name from property name when performing "Extract to constant in enclosing scope" on a PropertyAccessExpression #37898

@dtinth

Description

@dtinth

Search Terms

newLocal, Extract to constant in enclosing scope

Suggestion

When performing “Extract to constant in enclosing scope”, if the expression being extracted is a PropertyAccessExpression, I think it would be great if TypeScript used the expression’s name instead of "newLocal", if the name would not clash with other symbols in the scope or keywords.

My suggestion summarized as a patch:

diff --git a/src/services/refactors/extractSymbol.ts b/src/services/refactors/extractSymbol.ts
index 30dcaeaebf..c3b57c61b0 100644
--- a/src/services/refactors/extractSymbol.ts
+++ b/src/services/refactors/extractSymbol.ts
@@ -1009,7 +1009,9 @@ namespace ts.refactor.extractSymbol {
 
         // Make a unique name for the extracted variable
         const file = scope.getSourceFile();
-        const localNameText = getUniqueName(isClassLike(scope) ? "newProperty" : "newLocal", file);
+        const localNameText = isPropertyAccessExpression(node) && !isClassLike(scope) && !checker.resolveName(node.name.text, node, SymbolFlags.Value, false) && !isPrivateIdentifier(node.name) && !isKeyword(node.name.originalKeywordKind!)
+            ? node.name.text
+            : getUniqueName(isClassLike(scope) ? "newProperty" : "newLocal", file);
         const isJS = isInJSFile(scope);
 
         let variableType = isJS || !checker.isContextSensitive(node)

If this sounds good, I’m happy to come up with a proper PR and submit it. Would love to try contributing :).

Use Cases

This would save me a lot of time when I extract stuff to constant (I do this a lot). Without this feature I would have to:

  • Copy the name before performing the refactoring, so that I could paste it into the rename box.

    Kapture 2020-04-11 at 2 54 21

  • Sometimes I forgot to copy before extracting. I had to type the name manually.

  • That name may be long and the rename box doesn’t provide auto-completions… I had to dismiss the rename box, copy the name, and come back to the rename box.

    Kapture 2020-04-11 at 2 57 23

Examples

Kapture 2020-04-11 at 3 38 16

Text version
function get(options: { url: string }) {
  return fetch(options.url).then(r => r.json())
  //           ^^^^^^^^^^^
  //           Extract to constant in enclosing scope 
}

Current behavior: The variable name is always newLocal.

function get(options: { url: string }) {
  const newLocal = options.url;
  return fetch(newLocal).then(r => r.json())
}

Suggested behavior: The variable name is url.

function get(options: { url: string }) {
  const url = options.url;
  return fetch(url).then(r => r.json())
}

Checklist

My suggestion meets these guidelines:

  • This wouldn't be a breaking change in existing TypeScript/JavaScript code
  • This wouldn't change the runtime behavior of existing JavaScript code
  • This could be implemented without emitting different JS based on the types of the expressions
  • This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, etc.)
  • This feature would agree with the rest of TypeScript's Design Goals.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions