diff --git a/src/harness/unittests/extractConstants.ts b/src/harness/unittests/extractConstants.ts index 314790537e3..c5ddc18fea4 100644 --- a/src/harness/unittests/extractConstants.ts +++ b/src/harness/unittests/extractConstants.ts @@ -222,9 +222,21 @@ const f = () => { testExtractConstant("extractConstant_ArrowFunction_Expression", `const f = () => [#|2 + 1|];`); + + testExtractConstantFailed("extractConstant_Void", ` +function f(): void { } +[#|f();|]`); + + testExtractConstantFailed("extractConstant_Never", ` +function f(): never { } +[#|f();|]`); }); function testExtractConstant(caption: string, text: string) { testExtractSymbol(caption, text, "extractConstant", Diagnostics.Extract_constant); } + + function testExtractConstantFailed(caption: string, text: string) { + testExtractSymbolFailed(caption, text, Diagnostics.Extract_constant); + } } diff --git a/src/services/refactors/extractSymbol.ts b/src/services/refactors/extractSymbol.ts index 92c36fd1c99..0e2f5ebfe3c 100644 --- a/src/services/refactors/extractSymbol.ts +++ b/src/services/refactors/extractSymbol.ts @@ -127,6 +127,7 @@ namespace ts.refactor.extractSymbol { export const CannotExtractSuper: DiagnosticMessage = createMessage("Cannot extract super call."); export const CannotExtractEmpty: DiagnosticMessage = createMessage("Cannot extract empty range."); export const ExpressionExpected: DiagnosticMessage = createMessage("expression expected."); + export const UselessConstantType: DiagnosticMessage = createMessage("No reason to extract constant of type."); export const StatementOrExpressionExpected: DiagnosticMessage = createMessage("Statement or expression expected."); export const CannotExtractRangeContainingConditionalBreakOrContinueStatements: DiagnosticMessage = createMessage("Cannot extract range containing conditional break or continue statements."); export const CannotExtractRangeContainingConditionalReturnStatement: DiagnosticMessage = createMessage("Cannot extract range containing conditional return statement."); @@ -1278,11 +1279,23 @@ namespace ts.refactor.extractSymbol { const constantErrorsPerScope: Diagnostic[][] = []; const visibleDeclarationsInExtractedRange: Symbol[] = []; - const expressionDiagnostic = - isReadonlyArray(targetRange.range) && !(targetRange.range.length === 1 && isExpressionStatement(targetRange.range[0])) - ? ((start, end) => createFileDiagnostic(sourceFile, start, end - start, Messages.ExpressionExpected))(first(targetRange.range).getStart(), last(targetRange.range).end) + const expression = !isReadonlyArray(targetRange.range) + ? targetRange.range + : targetRange.range.length === 1 && isExpressionStatement(targetRange.range[0]) + ? (targetRange.range[0] as ExpressionStatement).expression : undefined; + let expressionDiagnostic: Diagnostic | undefined = undefined; + if (expression === undefined) { + const statements = targetRange.range as ReadonlyArray; + const start = first(statements).getStart(); + const end = last(statements).end; + expressionDiagnostic = createFileDiagnostic(sourceFile, start, end - start, Messages.ExpressionExpected); + } + else if (checker.getTypeAtLocation(expression).flags & (TypeFlags.Void | TypeFlags.Never)) { + expressionDiagnostic = createDiagnosticForNode(expression, Messages.UselessConstantType); + } + // initialize results for (const scope of scopes) { usagesPerScope.push({ usages: createMap(), typeParameterUsages: createMap(), substitutions: createMap<() => Expression>() });