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 ebca423

Browse filesBrowse files
authored
Add elaboration & quickfix for async-able arrow function (microsoft#36342)
1 parent 6b645f5 commit ebca423
Copy full SHA for ebca423

7 files changed

+171Lines changed: 171 additions & 0 deletions

File tree

Expand file treeCollapse file tree
Open diff view settings
Filter options
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎src/compiler/checker.ts‎

Copy file name to clipboardExpand all lines: src/compiler/checker.ts
+11Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14153,6 +14153,17 @@ namespace ts {
1415314153
Diagnostics.The_expected_type_comes_from_the_return_type_of_this_signature,
1415414154
));
1415514155
}
14156+
if ((getFunctionFlags(node) & FunctionFlags.Async) === 0
14157+
// exclude cases where source itself is promisy - this way we don't make a suggestion when relating
14158+
// an IPromise and a Promise that are slightly different
14159+
&& !getTypeOfPropertyOfType(sourceReturn, "then" as __String)
14160+
&& checkTypeRelatedTo(createPromiseType(sourceReturn), targetReturn, relation, /*errorNode*/ undefined)
14161+
) {
14162+
addRelatedInfo(resultObj.errors[resultObj.errors.length - 1], createDiagnosticForNode(
14163+
node,
14164+
Diagnostics.Did_you_mean_to_mark_this_function_as_async
14165+
));
14166+
}
1415614167
return true;
1415714168
}
1415814169
}
Collapse file
+87Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/* @internal */
2+
namespace ts.codefix {
3+
type ContextualTrackChangesFunction = (cb: (changeTracker: textChanges.ChangeTracker) => void) => FileTextChanges[];
4+
const fixId = "addMissingAsync";
5+
const errorCodes = [
6+
Diagnostics.Argument_of_type_0_is_not_assignable_to_parameter_of_type_1.code,
7+
Diagnostics.Type_0_is_not_assignable_to_type_1.code,
8+
Diagnostics.Type_0_is_not_comparable_to_type_1.code
9+
];
10+
11+
registerCodeFix({
12+
fixIds: [fixId],
13+
errorCodes,
14+
getCodeActions: context => {
15+
const { sourceFile, errorCode, cancellationToken, program, span } = context;
16+
const diagnostic = find(program.getDiagnosticsProducingTypeChecker().getDiagnostics(sourceFile, cancellationToken), getIsMatchingAsyncError(span, errorCode));
17+
const directSpan = diagnostic && diagnostic.relatedInformation && find(diagnostic.relatedInformation, r => r.code === Diagnostics.Did_you_mean_to_mark_this_function_as_async.code) as TextSpan | undefined;
18+
19+
const decl = getFixableErrorSpanDeclaration(sourceFile, directSpan);
20+
if (!decl) {
21+
return;
22+
}
23+
24+
const trackChanges: ContextualTrackChangesFunction = cb => textChanges.ChangeTracker.with(context, cb);
25+
return [getFix(context, decl, trackChanges)];
26+
},
27+
getAllCodeActions: context => {
28+
const { sourceFile } = context;
29+
const fixedDeclarations = createMap<true>();
30+
return codeFixAll(context, errorCodes, (t, diagnostic) => {
31+
const span = diagnostic.relatedInformation && find(diagnostic.relatedInformation, r => r.code === Diagnostics.Did_you_mean_to_mark_this_function_as_async.code) as TextSpan | undefined;
32+
const decl = getFixableErrorSpanDeclaration(sourceFile, span);
33+
if (!decl) {
34+
return;
35+
}
36+
const trackChanges: ContextualTrackChangesFunction = cb => (cb(t), []);
37+
return getFix(context, decl, trackChanges, fixedDeclarations);
38+
});
39+
},
40+
});
41+
42+
type FixableDeclaration = ArrowFunction | FunctionDeclaration | FunctionExpression | MethodDeclaration;
43+
function getFix(context: CodeFixContext | CodeFixAllContext, decl: FixableDeclaration, trackChanges: ContextualTrackChangesFunction, fixedDeclarations?: Map<true>) {
44+
const changes = trackChanges(t => makeChange(t, context.sourceFile, decl, fixedDeclarations));
45+
return createCodeFixAction(fixId, changes, Diagnostics.Add_async_modifier_to_containing_function, fixId, Diagnostics.Add_all_missing_async_modifiers);
46+
}
47+
48+
function makeChange(changeTracker: textChanges.ChangeTracker, sourceFile: SourceFile, insertionSite: FixableDeclaration, fixedDeclarations?: Map<true>) {
49+
if (fixedDeclarations) {
50+
if (fixedDeclarations.has(getNodeId(insertionSite).toString())) {
51+
return;
52+
}
53+
}
54+
fixedDeclarations?.set(getNodeId(insertionSite).toString(), true);
55+
const cloneWithModifier = getSynthesizedDeepClone(insertionSite, /*includeTrivia*/ true);
56+
cloneWithModifier.modifiers = createNodeArray(createModifiersFromModifierFlags(getModifierFlags(insertionSite) | ModifierFlags.Async));
57+
cloneWithModifier.modifierFlagsCache = 0;
58+
changeTracker.replaceNode(
59+
sourceFile,
60+
insertionSite,
61+
cloneWithModifier);
62+
}
63+
64+
function getFixableErrorSpanDeclaration(sourceFile: SourceFile, span: TextSpan | undefined): FixableDeclaration | undefined {
65+
if (!span) return undefined;
66+
const token = getTokenAtPosition(sourceFile, span.start);
67+
// Checker has already done work to determine that async might be possible, and has attached
68+
// related info to the node, so start by finding the signature that exactly matches up
69+
// with the diagnostic range.
70+
const decl = findAncestor(token, node => {
71+
if (node.getStart(sourceFile) < span.start || node.getEnd() > textSpanEnd(span)) {
72+
return "quit";
73+
}
74+
return (isArrowFunction(node) || isMethodDeclaration(node) || isFunctionExpression(node) || isFunctionDeclaration(node)) && textSpansEqual(span, createTextSpanFromNode(node, sourceFile));
75+
}) as FixableDeclaration | undefined;
76+
77+
return decl;
78+
}
79+
80+
function getIsMatchingAsyncError(span: TextSpan, errorCode: number) {
81+
return ({ start, length, relatedInformation, code }: Diagnostic) =>
82+
isNumber(start) && isNumber(length) && textSpansEqual({ start, length }, span) &&
83+
code === errorCode &&
84+
!!relatedInformation &&
85+
some(relatedInformation, related => related.code === Diagnostics.Did_you_mean_to_mark_this_function_as_async.code);
86+
}
87+
}
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
@@ -48,6 +48,7 @@
4848
"refactorProvider.ts",
4949
"codefixes/addConvertToUnknownForNonOverlappingTypes.ts",
5050
"codefixes/addEmptyExportDeclaration.ts",
51+
"codefixes/addMissingAsync.ts",
5152
"codefixes/addMissingAwait.ts",
5253
"codefixes/addMissingConst.ts",
5354
"codefixes/addMissingDeclareProperty.ts",
Collapse file

‎tests/baselines/reference/errorOnUnionVsObjectShouldDeeplyDisambiguate.errors.txt‎

Copy file name to clipboardExpand all lines: tests/baselines/reference/errorOnUnionVsObjectShouldDeeplyDisambiguate.errors.txt
+10Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,41 +37,51 @@ tests/cases/compiler/errorOnUnionVsObjectShouldDeeplyDisambiguate.ts(27,16): err
3737
~~~~~~~
3838
!!! error TS2322: Type 'string' is not assignable to type 'Promise<string>'.
3939
!!! related TS6502 tests/cases/compiler/errorOnUnionVsObjectShouldDeeplyDisambiguate.ts:3:8: The expected type comes from the return type of this signature.
40+
!!! related TS1356 tests/cases/compiler/errorOnUnionVsObjectShouldDeeplyDisambiguate.ts:18:10: Did you mean to mark this function as 'async'?
4041
c: () => "hello",
4142
~~~~~~~
4243
!!! error TS2322: Type 'string' is not assignable to type 'Promise<string>'.
4344
!!! related TS6502 tests/cases/compiler/errorOnUnionVsObjectShouldDeeplyDisambiguate.ts:4:8: The expected type comes from the return type of this signature.
45+
!!! related TS1356 tests/cases/compiler/errorOnUnionVsObjectShouldDeeplyDisambiguate.ts:19:10: Did you mean to mark this function as 'async'?
4446
d: () => "hello",
4547
~~~~~~~
4648
!!! error TS2322: Type 'string' is not assignable to type 'Promise<string>'.
4749
!!! related TS6502 tests/cases/compiler/errorOnUnionVsObjectShouldDeeplyDisambiguate.ts:5:8: The expected type comes from the return type of this signature.
50+
!!! related TS1356 tests/cases/compiler/errorOnUnionVsObjectShouldDeeplyDisambiguate.ts:20:10: Did you mean to mark this function as 'async'?
4851
e: () => "hello",
4952
~~~~~~~
5053
!!! error TS2322: Type 'string' is not assignable to type 'Promise<string>'.
5154
!!! related TS6502 tests/cases/compiler/errorOnUnionVsObjectShouldDeeplyDisambiguate.ts:6:8: The expected type comes from the return type of this signature.
55+
!!! related TS1356 tests/cases/compiler/errorOnUnionVsObjectShouldDeeplyDisambiguate.ts:21:10: Did you mean to mark this function as 'async'?
5256
f: () => "hello",
5357
~~~~~~~
5458
!!! error TS2322: Type 'string' is not assignable to type 'Promise<string>'.
5559
!!! related TS6502 tests/cases/compiler/errorOnUnionVsObjectShouldDeeplyDisambiguate.ts:7:8: The expected type comes from the return type of this signature.
60+
!!! related TS1356 tests/cases/compiler/errorOnUnionVsObjectShouldDeeplyDisambiguate.ts:22:10: Did you mean to mark this function as 'async'?
5661
g: () => "hello",
5762
~~~~~~~
5863
!!! error TS2322: Type 'string' is not assignable to type 'Promise<string>'.
5964
!!! related TS6502 tests/cases/compiler/errorOnUnionVsObjectShouldDeeplyDisambiguate.ts:8:8: The expected type comes from the return type of this signature.
65+
!!! related TS1356 tests/cases/compiler/errorOnUnionVsObjectShouldDeeplyDisambiguate.ts:23:10: Did you mean to mark this function as 'async'?
6066
h: () => "hello",
6167
~~~~~~~
6268
!!! error TS2322: Type 'string' is not assignable to type 'Promise<string>'.
6369
!!! related TS6502 tests/cases/compiler/errorOnUnionVsObjectShouldDeeplyDisambiguate.ts:9:8: The expected type comes from the return type of this signature.
70+
!!! related TS1356 tests/cases/compiler/errorOnUnionVsObjectShouldDeeplyDisambiguate.ts:24:10: Did you mean to mark this function as 'async'?
6471
i: () => "hello",
6572
~~~~~~~
6673
!!! error TS2322: Type 'string' is not assignable to type 'Promise<string>'.
6774
!!! related TS6502 tests/cases/compiler/errorOnUnionVsObjectShouldDeeplyDisambiguate.ts:10:8: The expected type comes from the return type of this signature.
75+
!!! related TS1356 tests/cases/compiler/errorOnUnionVsObjectShouldDeeplyDisambiguate.ts:25:10: Did you mean to mark this function as 'async'?
6876
j: () => "hello",
6977
~~~~~~~
7078
!!! error TS2322: Type 'string' is not assignable to type 'Promise<string>'.
7179
!!! related TS6502 tests/cases/compiler/errorOnUnionVsObjectShouldDeeplyDisambiguate.ts:11:8: The expected type comes from the return type of this signature.
80+
!!! related TS1356 tests/cases/compiler/errorOnUnionVsObjectShouldDeeplyDisambiguate.ts:26:10: Did you mean to mark this function as 'async'?
7281
k: () => 123
7382
~~~
7483
!!! error TS2322: Type 'number' is not assignable to type 'Promise<number>'.
7584
!!! related TS6502 tests/cases/compiler/errorOnUnionVsObjectShouldDeeplyDisambiguate.ts:12:8: The expected type comes from the return type of this signature.
85+
!!! related TS1356 tests/cases/compiler/errorOnUnionVsObjectShouldDeeplyDisambiguate.ts:27:10: Did you mean to mark this function as 'async'?
7686
}
7787
}
Collapse file

‎tests/baselines/reference/errorOnUnionVsObjectShouldDeeplyDisambiguate2.errors.txt‎

Copy file name to clipboardExpand all lines: tests/baselines/reference/errorOnUnionVsObjectShouldDeeplyDisambiguate2.errors.txt
+10Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,41 +37,51 @@ tests/cases/compiler/errorOnUnionVsObjectShouldDeeplyDisambiguate2.ts(27,14): er
3737
~~~~~~~
3838
!!! error TS2322: Type 'string' is not assignable to type 'Promise<string>'.
3939
!!! related TS6502 tests/cases/compiler/errorOnUnionVsObjectShouldDeeplyDisambiguate2.ts:3:6: The expected type comes from the return type of this signature.
40+
!!! related TS1356 tests/cases/compiler/errorOnUnionVsObjectShouldDeeplyDisambiguate2.ts:18:8: Did you mean to mark this function as 'async'?
4041
c: () => "hello",
4142
~~~~~~~
4243
!!! error TS2322: Type 'string' is not assignable to type 'Promise<string>'.
4344
!!! related TS6502 tests/cases/compiler/errorOnUnionVsObjectShouldDeeplyDisambiguate2.ts:4:6: The expected type comes from the return type of this signature.
45+
!!! related TS1356 tests/cases/compiler/errorOnUnionVsObjectShouldDeeplyDisambiguate2.ts:19:8: Did you mean to mark this function as 'async'?
4446
d: () => "hello",
4547
~~~~~~~
4648
!!! error TS2322: Type 'string' is not assignable to type 'Promise<string>'.
4749
!!! related TS6502 tests/cases/compiler/errorOnUnionVsObjectShouldDeeplyDisambiguate2.ts:5:6: The expected type comes from the return type of this signature.
50+
!!! related TS1356 tests/cases/compiler/errorOnUnionVsObjectShouldDeeplyDisambiguate2.ts:20:8: Did you mean to mark this function as 'async'?
4851
e: () => "hello",
4952
~~~~~~~
5053
!!! error TS2322: Type 'string' is not assignable to type 'Promise<string>'.
5154
!!! related TS6502 tests/cases/compiler/errorOnUnionVsObjectShouldDeeplyDisambiguate2.ts:6:6: The expected type comes from the return type of this signature.
55+
!!! related TS1356 tests/cases/compiler/errorOnUnionVsObjectShouldDeeplyDisambiguate2.ts:21:8: Did you mean to mark this function as 'async'?
5256
f: () => "hello",
5357
~~~~~~~
5458
!!! error TS2322: Type 'string' is not assignable to type 'Promise<string>'.
5559
!!! related TS6502 tests/cases/compiler/errorOnUnionVsObjectShouldDeeplyDisambiguate2.ts:7:6: The expected type comes from the return type of this signature.
60+
!!! related TS1356 tests/cases/compiler/errorOnUnionVsObjectShouldDeeplyDisambiguate2.ts:22:8: Did you mean to mark this function as 'async'?
5661
g: () => "hello",
5762
~~~~~~~
5863
!!! error TS2322: Type 'string' is not assignable to type 'Promise<string>'.
5964
!!! related TS6502 tests/cases/compiler/errorOnUnionVsObjectShouldDeeplyDisambiguate2.ts:8:6: The expected type comes from the return type of this signature.
65+
!!! related TS1356 tests/cases/compiler/errorOnUnionVsObjectShouldDeeplyDisambiguate2.ts:23:8: Did you mean to mark this function as 'async'?
6066
h: () => "hello",
6167
~~~~~~~
6268
!!! error TS2322: Type 'string' is not assignable to type 'Promise<string>'.
6369
!!! related TS6502 tests/cases/compiler/errorOnUnionVsObjectShouldDeeplyDisambiguate2.ts:9:6: The expected type comes from the return type of this signature.
70+
!!! related TS1356 tests/cases/compiler/errorOnUnionVsObjectShouldDeeplyDisambiguate2.ts:24:8: Did you mean to mark this function as 'async'?
6471
i: () => "hello",
6572
~~~~~~~
6673
!!! error TS2322: Type 'string' is not assignable to type 'Promise<string>'.
6774
!!! related TS6502 tests/cases/compiler/errorOnUnionVsObjectShouldDeeplyDisambiguate2.ts:10:6: The expected type comes from the return type of this signature.
75+
!!! related TS1356 tests/cases/compiler/errorOnUnionVsObjectShouldDeeplyDisambiguate2.ts:25:8: Did you mean to mark this function as 'async'?
6876
j: () => "hello",
6977
~~~~~~~
7078
!!! error TS2322: Type 'string' is not assignable to type 'Promise<string>'.
7179
!!! related TS6502 tests/cases/compiler/errorOnUnionVsObjectShouldDeeplyDisambiguate2.ts:11:6: The expected type comes from the return type of this signature.
80+
!!! related TS1356 tests/cases/compiler/errorOnUnionVsObjectShouldDeeplyDisambiguate2.ts:26:8: Did you mean to mark this function as 'async'?
7281
k: () => 123
7382
~~~
7483
!!! error TS2322: Type 'number' is not assignable to type 'Promise<number>'.
7584
!!! related TS6502 tests/cases/compiler/errorOnUnionVsObjectShouldDeeplyDisambiguate2.ts:12:6: The expected type comes from the return type of this signature.
85+
!!! related TS1356 tests/cases/compiler/errorOnUnionVsObjectShouldDeeplyDisambiguate2.ts:27:8: Did you mean to mark this function as 'async'?
7686
}
7787
}
Collapse file
+26Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/// <reference path="fourslash.ts" />
2+
////interface Stuff {
3+
//// b: () => Promise<string>;
4+
////}
5+
////
6+
////function foo(): Stuff | Date {
7+
//// return {
8+
//// b: () => "hello",
9+
//// }
10+
////}
11+
12+
verify.codeFix({
13+
description: ts.Diagnostics.Add_async_modifier_to_containing_function.message,
14+
index: 0,
15+
newFileContent:
16+
`interface Stuff {
17+
b: () => Promise<string>;
18+
}
19+
20+
function foo(): Stuff | Date {
21+
return {
22+
b: async () => "hello",
23+
}
24+
}`
25+
});
26+
Collapse file
+26Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/// <reference path="fourslash.ts" />
2+
////interface Stuff {
3+
//// b: () => Promise<string>;
4+
////}
5+
////
6+
////function foo(): Stuff | Date {
7+
//// return {
8+
//// b: _ => "hello",
9+
//// }
10+
////}
11+
12+
verify.codeFix({
13+
description: ts.Diagnostics.Add_async_modifier_to_containing_function.message,
14+
index: 0,
15+
newFileContent:
16+
`interface Stuff {
17+
b: () => Promise<string>;
18+
}
19+
20+
function foo(): Stuff | Date {
21+
return {
22+
b: async (_) => "hello",
23+
}
24+
}`
25+
});
26+

0 commit comments

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