diff --git a/src/services/refactors/extractSymbol.ts b/src/services/refactors/extractSymbol.ts
index 9f0392f1014..37310c041e8 100644
--- a/src/services/refactors/extractSymbol.ts
+++ b/src/services/refactors/extractSymbol.ts
@@ -433,7 +433,7 @@ namespace ts.refactor.extractSymbol {
// For understanding how skipTrivia functioned:
Debug.assert(!positionIsSynthesized(nodeToCheck.pos), "This failure could trigger https://github.com/Microsoft/TypeScript/issues/20809 (2)");
- if (!isStatement(nodeToCheck) && !(isExpressionNode(nodeToCheck) && isExtractableExpression(nodeToCheck))) {
+ if (!isStatement(nodeToCheck) && !(isExpressionNode(nodeToCheck) && isExtractableExpression(nodeToCheck)) && !isStringLiteralJsxAttribute(nodeToCheck)) {
return [createDiagnosticForNode(nodeToCheck, Messages.statementOrExpressionExpected)];
}
@@ -625,13 +625,16 @@ namespace ts.refactor.extractSymbol {
if (isStatement(node)) {
return [node];
}
- else if (isExpressionNode(node)) {
+ if (isExpressionNode(node)) {
// If our selection is the expression in an ExpressionStatement, expand
// the selection to include the enclosing Statement (this stops us
// from trying to care about the return value of the extracted function
// and eliminates double semicolon insertion in certain scenarios)
return isExpressionStatement(node.parent) ? [node.parent] : node as Expression;
}
+ if (isStringLiteralJsxAttribute(node)) {
+ return node;
+ }
return undefined;
}
@@ -1649,7 +1652,7 @@ namespace ts.refactor.extractSymbol {
// Unfortunately, this code takes advantage of the knowledge that the generated method
// will use the contextual type of an expression as the return type of the extracted
// method (and will therefore "use" all the types involved).
- if (inGenericContext && !isReadonlyArray(targetRange.range)) {
+ if (inGenericContext && !isReadonlyArray(targetRange.range) && !isJsxAttribute(targetRange.range)) {
const contextualType = checker.getContextualType(targetRange.range)!; // TODO: GH#18217
recordTypeParameterUsages(contextualType);
}
@@ -1997,6 +2000,11 @@ namespace ts.refactor.extractSymbol {
}
function isInJSXContent(node: Node) {
- return (isJsxElement(node) || isJsxSelfClosingElement(node) || isJsxFragment(node)) && (isJsxElement(node.parent) || isJsxFragment(node.parent));
+ return isStringLiteralJsxAttribute(node) ||
+ (isJsxElement(node) || isJsxSelfClosingElement(node) || isJsxFragment(node)) && (isJsxElement(node.parent) || isJsxFragment(node.parent));
+ }
+
+ function isStringLiteralJsxAttribute(node: Node): node is StringLiteral {
+ return isStringLiteral(node) && node.parent && isJsxAttribute(node.parent);
}
}
diff --git a/tests/cases/fourslash/extract-const_jsxAttribute1.ts b/tests/cases/fourslash/extract-const_jsxAttribute1.ts
new file mode 100644
index 00000000000..f1962afece1
--- /dev/null
+++ b/tests/cases/fourslash/extract-const_jsxAttribute1.ts
@@ -0,0 +1,28 @@
+///