Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 0904865

Browse filesBrowse files
authored
feat(40750): add refactoring to infer a return type annotation to a function (microsoft#41052)
1 parent 3192754 commit 0904865
Copy full SHA for 0904865

19 files changed

+367-1Lines changed: 367 additions & 1 deletion
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎src/compiler/diagnosticMessages.json‎

Copy file name to clipboardExpand all lines: src/compiler/diagnosticMessages.json
+4Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5979,6 +5979,10 @@
59795979
"category": "Message",
59805980
"code": 95147
59815981
},
5982+
"Infer function return type": {
5983+
"category": "Message",
5984+
"code": 95148
5985+
},
59825986

59835987
"No value exists in scope for the shorthand property '{0}'. Either declare one or provide an initializer.": {
59845988
"category": "Error",
Collapse file
+84Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/* @internal */
2+
namespace ts.refactor.inferFunctionReturnType {
3+
const refactorName = "Infer function return type";
4+
const refactorDescription = Diagnostics.Infer_function_return_type.message;
5+
registerRefactor(refactorName, { getEditsForAction, getAvailableActions });
6+
7+
function getEditsForAction(context: RefactorContext): RefactorEditInfo | undefined {
8+
const info = getInfo(context);
9+
if (info) {
10+
const edits = textChanges.ChangeTracker.with(context, t =>
11+
t.tryInsertTypeAnnotation(context.file, info.declaration, info.returnTypeNode));
12+
return { renameFilename: undefined, renameLocation: undefined, edits };
13+
}
14+
return undefined;
15+
}
16+
17+
function getAvailableActions(context: RefactorContext): readonly ApplicableRefactorInfo[] {
18+
const info = getInfo(context);
19+
if (info) {
20+
return [{
21+
name: refactorName,
22+
description: refactorDescription,
23+
actions: [{
24+
name: refactorName,
25+
description: refactorDescription
26+
}]
27+
}];
28+
}
29+
return emptyArray;
30+
}
31+
32+
type ConvertibleDeclaration =
33+
| FunctionDeclaration
34+
| FunctionExpression
35+
| ArrowFunction
36+
| MethodDeclaration;
37+
38+
interface Info {
39+
declaration: ConvertibleDeclaration;
40+
returnTypeNode: TypeNode;
41+
}
42+
43+
function getInfo(context: RefactorContext): Info | undefined {
44+
if (isInJSFile(context.file)) return;
45+
46+
const token = getTokenAtPosition(context.file, context.startPosition);
47+
const declaration = findAncestor(token, isConvertibleDeclaration);
48+
if (!declaration || !declaration.body || declaration.type) return;
49+
50+
const typeChecker = context.program.getTypeChecker();
51+
const returnType = tryGetReturnType(typeChecker, declaration);
52+
if (!returnType) return;
53+
54+
const returnTypeNode = typeChecker.typeToTypeNode(returnType, declaration, NodeBuilderFlags.NoTruncation);
55+
if (returnTypeNode) {
56+
return { declaration, returnTypeNode };
57+
}
58+
}
59+
60+
function isConvertibleDeclaration(node: Node): node is ConvertibleDeclaration {
61+
switch (node.kind) {
62+
case SyntaxKind.FunctionDeclaration:
63+
case SyntaxKind.FunctionExpression:
64+
case SyntaxKind.ArrowFunction:
65+
case SyntaxKind.MethodDeclaration:
66+
return true;
67+
default:
68+
return false;
69+
}
70+
}
71+
72+
function tryGetReturnType(typeChecker: TypeChecker, node: ConvertibleDeclaration): Type | undefined {
73+
if (typeChecker.isImplementationOfOverload(node)) {
74+
const signatures = typeChecker.getTypeAtLocation(node).getCallSignatures();
75+
if (signatures.length > 1) {
76+
return typeChecker.getUnionType(mapDefined(signatures, s => s.getReturnType()));
77+
}
78+
}
79+
const signature = typeChecker.getSignatureFromDeclaration(node);
80+
if (signature) {
81+
return typeChecker.getReturnTypeOfSignature(signature);
82+
}
83+
}
84+
}
Collapse file

‎src/services/tsconfig.json‎

Copy file name to clipboardExpand all lines: src/services/tsconfig.json
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@
121121
"refactors/convertParamsToDestructuredObject.ts",
122122
"refactors/convertStringOrTemplateLiteral.ts",
123123
"refactors/convertArrowFunctionOrFunctionExpression.ts",
124+
"refactors/inferFunctionReturnType.ts",
124125
"services.ts",
125126
"breakpoints.ts",
126127
"transform.ts",
Collapse file

‎tests/cases/fourslash/refactorConvertExport_namedToDefault_alreadyHasDefault.ts‎

Copy file name to clipboardExpand all lines: tests/cases/fourslash/refactorConvertExport_namedToDefault_alreadyHasDefault.ts
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@
55
////export default function g() {}
66

77
goTo.select("a", "b");
8-
verify.refactorsAvailable([]);
8+
verify.refactorsAvailable(["Infer function return type"]);
Collapse file
+16Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
////function /*a*/foo/*b*/() {
4+
//// return { x: 1, y: 1 };
5+
////}
6+
7+
goTo.select("a", "b");
8+
edit.applyRefactor({
9+
refactorName: "Infer function return type",
10+
actionName: "Infer function return type",
11+
actionDescription: "Infer function return type",
12+
newContent:
13+
`function foo(): { x: number; y: number; } {
14+
return { x: 1, y: 1 };
15+
}`
16+
});
Collapse file
+16Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
////function /*a*/foo/*b*/(x: number) {
4+
//// return x ? x : x > 1;
5+
////}
6+
7+
goTo.select("a", "b");
8+
edit.applyRefactor({
9+
refactorName: "Infer function return type",
10+
actionName: "Infer function return type",
11+
actionDescription: "Infer function return type",
12+
newContent:
13+
`function foo(x: number): number | boolean {
14+
return x ? x : x > 1;
15+
}`
16+
});
Collapse file
+36Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
////interface F1 { x: number; y: number; }
4+
////type T1 = [number, number];
5+
////
6+
////function /*a*/foo/*b*/(num: number) {
7+
//// switch (num) {
8+
//// case 1:
9+
//// return { x: num, y: num } as F1;
10+
//// case 2:
11+
//// return [num, num] as T1;
12+
//// default:
13+
//// return num;
14+
//// }
15+
////}
16+
17+
goTo.select("a", "b");
18+
edit.applyRefactor({
19+
refactorName: "Infer function return type",
20+
actionName: "Infer function return type",
21+
actionDescription: "Infer function return type",
22+
newContent:
23+
`interface F1 { x: number; y: number; }
24+
type T1 = [number, number];
25+
26+
function foo(num: number): number | F1 | T1 {
27+
switch (num) {
28+
case 1:
29+
return { x: num, y: num } as F1;
30+
case 2:
31+
return [num, num] as T1;
32+
default:
33+
return num;
34+
}
35+
}`
36+
});
Collapse file
+22Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
////function f(x: number) {
4+
//// return 1;
5+
////}
6+
////function /*a*/f/*b*/(x: number) {
7+
//// return "1";
8+
////}
9+
10+
goTo.select("a", "b");
11+
edit.applyRefactor({
12+
refactorName: "Infer function return type",
13+
actionName: "Infer function return type",
14+
actionDescription: "Infer function return type",
15+
newContent:
16+
`function f(x: number) {
17+
return 1;
18+
}
19+
function f(x: number): string {
20+
return "1";
21+
}`
22+
});
Collapse file
+9Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
////function /*a*/f/*b*/(x: string): number;
4+
////function f(x: string | number) {
5+
//// return 1;
6+
////}
7+
8+
goTo.select("a", "b");
9+
verify.not.refactorAvailable("Infer function return type");
Collapse file
+20Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
////function f(x: number): string;
4+
////function f(x: string): number;
5+
////function /*a*/f/*b*/(x: string | number) {
6+
//// return x === 1 ? 1 : "quit";
7+
////}
8+
9+
goTo.select("a", "b");
10+
edit.applyRefactor({
11+
refactorName: "Infer function return type",
12+
actionName: "Infer function return type",
13+
actionDescription: "Infer function return type",
14+
newContent:
15+
`function f(x: number): string;
16+
function f(x: string): number;
17+
function f(x: string | number): string | number {
18+
return x === 1 ? 1 : "quit";
19+
}`
20+
});

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.