Skip to content

Commit 7ec467e

Browse files
fix: extract const in jsx (#37912)
* fix: extract const in jsx * Update src/services/refactors/extractSymbol.ts Co-authored-by: Andrew Branch <[email protected]> * Update src/services/refactors/extractSymbol.ts Co-authored-by: Andrew Branch <[email protected]> Co-authored-by: Andrew Branch <[email protected]>
1 parent 46f100f commit 7ec467e

File tree

2 files changed

+37
-5
lines changed

2 files changed

+37
-5
lines changed

src/services/refactors/extractSymbol.ts

+16-5
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ namespace ts.refactor.extractSymbol {
2828
const usedConstantNames: Map<boolean> = createMap();
2929

3030
let i = 0;
31-
for (const {functionExtraction, constantExtraction} of extractions) {
31+
for (const { functionExtraction, constantExtraction } of extractions) {
3232
// Skip these since we don't have a way to report errors yet
3333
if (functionExtraction.errors.length === 0) {
3434
// Don't issue refactorings with duplicated names.
@@ -1103,7 +1103,12 @@ namespace ts.refactor.extractSymbol {
11031103
changeTracker.delete(context.file, node.parent);
11041104
}
11051105
else {
1106-
const localReference = createIdentifier(localNameText);
1106+
let localReference: Expression = createIdentifier(localNameText);
1107+
// When extract to a new variable in JSX content, need to wrap a {} out of the new variable
1108+
// or it will become a plain text
1109+
if (isInJSXContent(node)) {
1110+
localReference = createJsxExpression(/*dotDotDotToken*/ undefined, localReference);
1111+
}
11071112
changeTracker.replaceNode(context.file, node, localReference);
11081113
}
11091114
}
@@ -1115,6 +1120,12 @@ namespace ts.refactor.extractSymbol {
11151120
const renameLocation = getRenameLocation(edits, renameFilename, localNameText, /*isDeclaredBeforeUse*/ true);
11161121
return { renameFilename, renameLocation, edits };
11171122

1123+
function isInJSXContent(node: Node) {
1124+
if (!isJsxElement(node)) return false;
1125+
if (isJsxElement(node.parent)) return true;
1126+
return false;
1127+
}
1128+
11181129
function transformFunctionInitializerAndType(variableType: TypeNode | undefined, initializer: Expression): { variableType: TypeNode | undefined, initializer: Expression } {
11191130
// If no contextual type exists there is nothing to transfer to the function signature
11201131
if (variableType === undefined) return { variableType, initializer };
@@ -1215,8 +1226,8 @@ namespace ts.refactor.extractSymbol {
12151226
}
12161227

12171228
function compareTypesByDeclarationOrder(
1218-
{type: type1, declaration: declaration1}: {type: Type, declaration?: Declaration},
1219-
{type: type2, declaration: declaration2}: {type: Type, declaration?: Declaration}) {
1229+
{ type: type1, declaration: declaration1 }: { type: Type, declaration?: Declaration },
1230+
{ type: type2, declaration: declaration2 }: { type: Type, declaration?: Declaration }) {
12201231

12211232
return compareProperties(declaration1, declaration2, "pos", compareValues)
12221233
|| compareStringsCaseSensitive(
@@ -1621,7 +1632,7 @@ namespace ts.refactor.extractSymbol {
16211632
// a lot of properties, each of which the walker will visit. Unfortunately, the
16221633
// solution isn't as trivial as filtering to user types because of (e.g.) Array.
16231634
const symbolWalker = checker.getSymbolWalker(() => (cancellationToken.throwIfCancellationRequested(), true));
1624-
const {visitedTypes} = symbolWalker.walkType(type);
1635+
const { visitedTypes } = symbolWalker.walkType(type);
16251636

16261637
for (const visitedType of visitedTypes) {
16271638
if (visitedType.isTypeParameter()) {
+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
// GH#35372
4+
5+
// @jsx: preserve
6+
// @filename: main.tsx
7+
////function foo() {
8+
//// return <div>/*a*/<a>content</a>/*b*/</div>;
9+
////}
10+
11+
goTo.select("a", "b");
12+
edit.applyRefactor({
13+
refactorName: "Extract Symbol",
14+
actionName: "constant_scope_0",
15+
actionDescription: "Extract to constant in enclosing scope",
16+
newContent:
17+
`function foo() {
18+
const /*RENAME*/newLocal = <a>content</a>;
19+
return <div>{newLocal}</div>;
20+
}`
21+
});

0 commit comments

Comments
 (0)