Skip to content

Add quick fix to add 'void' to Promise resolved without value #40558

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Sep 15, 2020

Conversation

rbuckton
Copy link
Contributor

This adds a quick fix to add void to a Promise that is resolved without a value, i.e. new Promise(resolve => resolve()).

The quick fix is designed to work both in TypeScript, and a few limited cases of JavaScript as well:

TypeScript:

  • new Promise(resolve => resolve()) -> new Promise<void>(resolve => resolve())
  • new Promise<number>(resolve => resolve()) -> new Promise<number | void>(resolve => resolve())
  • new Promise<number | string>(resolve => resolve()) -> new Promise<number | string | void>(resolve => resolve())
  • new Promise<{ x: number } & { y: number }>(resolve => resolve()) -> new Promise<({ x: number } & { y: number }) | void>(resolve => resolve())

JavaScript:

As there is no way to specify type arguments directly using JSDoc in a JS file, this works by inserting or modifying an /** @type {} */(...) cast around the call to new Promise(...):

  • new Promise(resolve => resolve()) -> /** @type {Promise<void>} */(new Promise(resolve => resolve()))
  • /** @type {Promise<number>} */(new Promise(resolve => resolve())) -> /** @type {Promise<number | void>} */(new Promise(resolve => resolve()))
  • /** @type {Promise<number | string>} */(new Promise(resolve => resolve())) -> /** @type {Promise<number | string | void>} */(new Promise(resolve => resolve()))
  • /** @type {Promise<{ x: number } & { y: string }>} */(new Promise(resolve => resolve())) -> /** @type {Promise<({ x: number } & { y: string }) | void>} */(new Promise(resolve => resolve()))

The quick fix is not supported on a new Promise with a contextual type in either TS or JS currently, as that could have further-reaching side-effects

@typescript-bot typescript-bot added Author: Team For Uncommitted Bug PR for untriaged, rejected, closed or missing bug labels Sep 14, 2020
};

addSourceFile(Harness.Compiler.defaultLibFileName);
compilationOptions.lib?.forEach(addSourceFile);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
compilationOptions.lib?.forEach(addSourceFile);
ts.forEach(compilationOptions.lib, addSourceFile);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But why though? The only advantage ts.forEach has is that it shortcuts exit on return, otherwise the native forEach is faster. Also, the original code used lib?.forEach: https://github.com/microsoft/TypeScript/pull/40558/files/e348bc9aa81e2db7fe68e1150b80a1dbd4585a6d#diff-ef3d6509d8056f71e3e2ec4b4c766e6aL346

@rbuckton
Copy link
Contributor Author

@DanielRosenwasser: @andrewbranch suggested I add a unique error message for this case in checker. The best I've come up with is something like this:

Expected {0} arguments, but got {1}. Did you forget to add a 'void' type argument to your Promise?

Not sure I like the wording though. Any suggestions on a description?

@rbuckton
Copy link
Contributor Author

rbuckton commented Sep 14, 2020

Alternatives:

  1. Did you forget to add a 'void' type argument to your 'Promise'? (above)
  2. Did you forget to declare your 'Promise' as 'Promise<void>' or 'Promise<... | void>'?
  3. Did you forget to include 'void' in your type argument to 'Promise'?

(2) seems a tad repetitive and I'm worried the ... | void might be confusing. (3) seems the simplest of the three.

@rbuckton
Copy link
Contributor Author

I went with (3), but we can change it later if we come up with something better.


function makeChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, span: TextSpan, program: Program, seen?: Set<ParameterDeclaration>) {
const node = getTokenAtPosition(sourceFile, span.start);
if (!isIdentifier(node) || !isCallExpression(node.parent) || node.parent.expression !== node || node.parent.arguments.length !== 0) return;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wasn't this checked when the diagnostic was produced?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but I'd rather recheck then blindly assert. The the place where we produce this error is so distant from this location, it would be easy for someone to change something and break this. Most of the checks are fairly trivial though.

changes.insertText(sourceFile, typeArgument.end, needsParens ? ") | void" : " | void");
}
else {
// make sure the Promise is type is untyped (i.e., `unknown`)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wasn't this checked when the diagnostic was produced?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not quite. This error can be produced from the following case, but we can't apply a quick fix to it:

interface Obj {
  m(): Promise<number>;
}
const obj: Obj = {
 m: () => new Promise(resolve => resolve());
}

We don't check the type of the promise in the checker, just the arity of the call to resolve. Adding void to new Promise would result in an error, and I'm not quick-fixing the definition of m on the Obj interface, as it could be code you don't own (such as from an external library), or could have repercussions throughout the codebase as you introduce a void that wasn't previously there. Instead, I check whether the promise is untyped (unknown in TS, any in JS).

@rbuckton
Copy link
Contributor Author

@DanielRosenwasser: I'd like to get a perf test run in with the new change in checker, but I think this is probably fine to merge. Any perf regression would only be in cases where we are already reporting a diagnostic, so clean code isn't affected.
@typescript-bot perf test

@typescript-bot
Copy link
Collaborator

typescript-bot commented Sep 15, 2020

Heya @rbuckton, I've started to run the perf test suite on this PR at 54681bd. You can monitor the build here.

Update: The results are in!

@typescript-bot
Copy link
Collaborator

@rbuckton
The results of the perf run you requested are in!

Here they are:

Comparison Report - master..40558

Metric master 40558 Delta Best Worst
Angular - node (v10.16.3, x64)
Memory used 349,553k (± 0.02%) 349,663k (± 0.03%) +110k (+ 0.03%) 349,431k 349,903k
Parse Time 2.00s (± 0.56%) 2.01s (± 0.55%) +0.01s (+ 0.50%) 1.99s 2.04s
Bind Time 0.83s (± 2.21%) 0.83s (± 1.06%) -0.00s (- 0.12%) 0.82s 0.85s
Check Time 4.87s (± 0.48%) 4.91s (± 0.57%) +0.04s (+ 0.80%) 4.84s 4.96s
Emit Time 5.17s (± 0.60%) 5.20s (± 0.71%) +0.03s (+ 0.58%) 5.13s 5.29s
Total Time 12.88s (± 0.34%) 12.96s (± 0.51%) +0.07s (+ 0.57%) 12.80s 13.07s
Monaco - node (v10.16.3, x64)
Memory used 354,296k (± 0.03%) 354,333k (± 0.01%) +38k (+ 0.01%) 354,176k 354,421k
Parse Time 1.56s (± 0.64%) 1.56s (± 0.67%) +0.00s (+ 0.19%) 1.54s 1.59s
Bind Time 0.71s (± 0.91%) 0.71s (± 0.56%) +0.00s (+ 0.42%) 0.70s 0.72s
Check Time 5.02s (± 0.51%) 5.05s (± 0.45%) +0.03s (+ 0.58%) 4.99s 5.10s
Emit Time 2.74s (± 0.73%) 2.74s (± 0.42%) +0.00s (+ 0.07%) 2.71s 2.77s
Total Time 10.03s (± 0.41%) 10.07s (± 0.33%) +0.04s (+ 0.38%) 9.95s 10.11s
TFS - node (v10.16.3, x64)
Memory used 307,530k (± 0.01%) 307,552k (± 0.01%) +22k (+ 0.01%) 307,480k 307,645k
Parse Time 1.21s (± 0.30%) 1.22s (± 0.69%) +0.01s (+ 0.58%) 1.20s 1.24s
Bind Time 0.67s (± 0.78%) 0.67s (± 1.01%) +0.01s (+ 0.75%) 0.65s 0.68s
Check Time 4.50s (± 0.43%) 4.55s (± 0.58%) +0.05s (+ 1.09%) 4.50s 4.63s
Emit Time 2.91s (± 1.24%) 2.90s (± 1.31%) -0.01s (- 0.21%) 2.77s 2.96s
Total Time 9.29s (± 0.56%) 9.34s (± 0.40%) +0.05s (+ 0.56%) 9.29s 9.45s
material-ui - node (v10.16.3, x64)
Memory used 488,877k (± 0.01%) 488,908k (± 0.01%) +31k (+ 0.01%) 488,781k 489,023k
Parse Time 1.98s (± 0.34%) 2.00s (± 0.41%) +0.02s (+ 1.01%) 1.97s 2.01s
Bind Time 0.65s (± 0.62%) 0.65s (± 0.95%) +0.00s (+ 0.00%) 0.63s 0.66s
Check Time 13.36s (± 0.68%) 13.42s (± 0.72%) +0.06s (+ 0.43%) 13.21s 13.64s
Emit Time 0.00s (± 0.00%) 0.00s (± 0.00%) 0.00s ( NaN%) 0.00s 0.00s
Total Time 15.98s (± 0.59%) 16.06s (± 0.63%) +0.08s (+ 0.50%) 15.86s 16.31s
Angular - node (v12.1.0, x64)
Memory used 326,759k (± 0.01%) 326,801k (± 0.02%) +42k (+ 0.01%) 326,674k 326,958k
Parse Time 1.99s (± 0.83%) 2.00s (± 0.41%) +0.00s (+ 0.15%) 1.98s 2.01s
Bind Time 0.80s (± 0.45%) 0.81s (± 1.00%) +0.01s (+ 1.12%) 0.80s 0.83s
Check Time 4.77s (± 0.32%) 4.79s (± 0.62%) +0.02s (+ 0.34%) 4.72s 4.86s
Emit Time 5.34s (± 0.54%) 5.36s (± 0.46%) +0.02s (+ 0.32%) 5.29s 5.40s
Total Time 12.91s (± 0.35%) 12.96s (± 0.25%) +0.05s (+ 0.36%) 12.85s 13.02s
Monaco - node (v12.1.0, x64)
Memory used 336,491k (± 0.02%) 336,449k (± 0.02%) -42k (- 0.01%) 336,306k 336,573k
Parse Time 1.53s (± 0.78%) 1.54s (± 0.47%) +0.01s (+ 0.59%) 1.53s 1.56s
Bind Time 0.69s (± 0.68%) 0.69s (± 0.52%) +0.00s (+ 0.58%) 0.69s 0.70s
Check Time 4.86s (± 0.30%) 4.86s (± 0.63%) +0.00s (+ 0.04%) 4.79s 4.91s
Emit Time 2.80s (± 0.51%) 2.86s (± 1.96%) +0.06s (+ 2.00%) 2.78s 3.06s
Total Time 9.88s (± 0.23%) 9.95s (± 0.75%) +0.07s (+ 0.73%) 9.80s 10.18s
TFS - node (v12.1.0, x64)
Memory used 291,790k (± 0.02%) 291,850k (± 0.01%) +60k (+ 0.02%) 291,781k 291,919k
Parse Time 1.23s (± 0.69%) 1.24s (± 0.61%) +0.01s (+ 1.06%) 1.23s 1.26s
Bind Time 0.64s (± 0.92%) 0.65s (± 0.93%) +0.00s (+ 0.16%) 0.63s 0.66s
Check Time 4.43s (± 0.55%) 4.44s (± 0.51%) +0.01s (+ 0.32%) 4.39s 4.49s
Emit Time 2.91s (± 0.69%) 2.92s (± 0.82%) +0.01s (+ 0.21%) 2.85s 2.97s
Total Time 9.22s (± 0.40%) 9.25s (± 0.53%) +0.03s (+ 0.35%) 9.10s 9.34s
material-ui - node (v12.1.0, x64)
Memory used 466,912k (± 0.06%) 466,866k (± 0.06%) -46k (- 0.01%) 465,862k 467,162k
Parse Time 2.02s (± 0.77%) 2.02s (± 0.53%) -0.00s (- 0.10%) 1.99s 2.04s
Bind Time 0.64s (± 0.87%) 0.63s (± 0.78%) -0.00s (- 0.63%) 0.63s 0.65s
Check Time 11.94s (± 0.42%) 12.06s (± 0.84%) +0.12s (+ 1.01%) 11.86s 12.32s
Emit Time 0.00s (± 0.00%) 0.00s (± 0.00%) 0.00s ( NaN%) 0.00s 0.00s
Total Time 14.59s (± 0.36%) 14.71s (± 0.73%) +0.12s (+ 0.84%) 14.50s 14.99s
Angular - node (v8.9.0, x64)
Memory used 346,274k (± 0.03%) 346,244k (± 0.02%) -30k (- 0.01%) 346,072k 346,375k
Parse Time 2.60s (± 3.79%) 2.56s (± 0.57%) -0.04s (- 1.42%) 2.54s 2.61s
Bind Time 0.86s (± 0.64%) 0.87s (± 0.79%) +0.00s (+ 0.46%) 0.85s 0.88s
Check Time 5.53s (± 0.68%) 5.53s (± 0.60%) +0.00s (+ 0.02%) 5.46s 5.59s
Emit Time 6.13s (± 0.98%) 6.19s (± 0.94%) +0.06s (+ 1.00%) 6.08s 6.38s
Total Time 15.11s (± 0.82%) 15.15s (± 0.53%) +0.03s (+ 0.22%) 15.01s 15.38s
Monaco - node (v8.9.0, x64)
Memory used 355,544k (± 0.02%) 355,560k (± 0.01%) +16k (+ 0.00%) 355,455k 355,677k
Parse Time 1.89s (± 0.83%) 1.89s (± 0.21%) +0.00s (+ 0.21%) 1.88s 1.90s
Bind Time 0.89s (± 0.84%) 0.89s (± 0.77%) +0.01s (+ 0.90%) 0.88s 0.91s
Check Time 5.58s (± 0.47%) 5.60s (± 0.53%) +0.02s (+ 0.39%) 5.54s 5.67s
Emit Time 3.26s (± 1.29%) 3.27s (± 1.63%) +0.01s (+ 0.37%) 3.17s 3.35s
Total Time 11.61s (± 0.57%) 11.66s (± 0.51%) +0.04s (+ 0.37%) 11.53s 11.80s
TFS - node (v8.9.0, x64)
Memory used 309,207k (± 0.01%) 309,214k (± 0.01%) +7k (+ 0.00%) 309,134k 309,274k
Parse Time 1.55s (± 0.56%) 1.55s (± 0.52%) -0.00s (- 0.13%) 1.53s 1.56s
Bind Time 0.67s (± 0.88%) 0.68s (± 0.54%) +0.00s (+ 0.30%) 0.67s 0.68s
Check Time 5.27s (± 0.49%) 5.30s (± 0.59%) +0.03s (+ 0.57%) 5.24s 5.36s
Emit Time 2.94s (± 1.29%) 2.96s (± 0.66%) +0.02s (+ 0.72%) 2.92s 3.01s
Total Time 10.43s (± 0.53%) 10.48s (± 0.37%) +0.05s (+ 0.51%) 10.40s 10.54s
material-ui - node (v8.9.0, x64)
Memory used 493,251k (± 0.01%) 493,321k (± 0.02%) +70k (+ 0.01%) 493,176k 493,587k
Parse Time 2.40s (± 0.28%) 2.41s (± 0.54%) +0.01s (+ 0.54%) 2.38s 2.44s
Bind Time 0.80s (± 1.24%) 0.81s (± 1.11%) +0.00s (+ 0.37%) 0.79s 0.82s
Check Time 18.00s (± 0.91%) 17.95s (± 0.65%) -0.05s (- 0.30%) 17.67s 18.14s
Emit Time 0.00s (± 0.00%) 0.00s (± 0.00%) 0.00s ( NaN%) 0.00s 0.00s
Total Time 21.20s (± 0.78%) 21.16s (± 0.54%) -0.04s (- 0.18%) 20.88s 21.35s
Angular - node (v8.9.0, x86)
Memory used 198,560k (± 0.03%) 198,545k (± 0.03%) -15k (- 0.01%) 198,428k 198,679k
Parse Time 2.47s (± 0.28%) 2.49s (± 0.64%) +0.02s (+ 0.77%) 2.46s 2.53s
Bind Time 1.01s (± 1.04%) 1.02s (± 1.21%) +0.01s (+ 0.49%) 0.98s 1.04s
Check Time 4.95s (± 0.62%) 4.98s (± 0.42%) +0.03s (+ 0.55%) 4.93s 5.02s
Emit Time 5.93s (± 1.02%) 5.95s (± 0.89%) +0.02s (+ 0.25%) 5.79s 6.07s
Total Time 14.36s (± 0.51%) 14.43s (± 0.44%) +0.06s (+ 0.45%) 14.26s 14.57s
Monaco - node (v8.9.0, x86)
Memory used 201,342k (± 0.01%) 201,345k (± 0.02%) +3k (+ 0.00%) 201,243k 201,405k
Parse Time 1.94s (± 1.36%) 1.93s (± 0.60%) -0.01s (- 0.46%) 1.90s 1.95s
Bind Time 0.71s (± 0.52%) 0.71s (± 0.91%) +0.00s (+ 0.28%) 0.70s 0.73s
Check Time 5.51s (± 1.60%) 5.43s (± 1.10%) -0.08s (- 1.49%) 5.34s 5.65s
Emit Time 2.94s (± 4.17%) 3.02s (± 2.80%) +0.08s (+ 2.86%) 2.69s 3.12s
Total Time 11.09s (± 0.60%) 11.08s (± 0.38%) -0.01s (- 0.11%) 10.95s 11.16s
TFS - node (v8.9.0, x86)
Memory used 176,720k (± 0.03%) 176,758k (± 0.04%) +38k (+ 0.02%) 176,603k 176,968k
Parse Time 1.58s (± 0.57%) 1.59s (± 0.53%) +0.01s (+ 0.51%) 1.57s 1.61s
Bind Time 0.64s (± 0.69%) 0.65s (± 0.77%) +0.00s (+ 0.47%) 0.64s 0.66s
Check Time 4.78s (± 0.57%) 4.82s (± 0.42%) +0.04s (+ 0.88%) 4.78s 4.86s
Emit Time 2.81s (± 0.84%) 2.79s (± 0.55%) -0.02s (- 0.68%) 2.75s 2.82s
Total Time 9.81s (± 0.38%) 9.85s (± 0.36%) +0.04s (+ 0.38%) 9.78s 9.94s
material-ui - node (v8.9.0, x86)
Memory used 277,675k (± 0.01%) 277,665k (± 0.02%) -10k (- 0.00%) 277,508k 277,762k
Parse Time 2.46s (± 0.54%) 2.47s (± 0.57%) +0.01s (+ 0.53%) 2.44s 2.50s
Bind Time 0.72s (± 6.52%) 0.74s (± 6.41%) +0.01s (+ 1.80%) 0.67s 0.84s
Check Time 16.30s (± 0.40%) 16.36s (± 0.73%) +0.06s (+ 0.36%) 16.10s 16.59s
Emit Time 0.00s (± 0.00%) 0.00s (± 0.00%) 0.00s ( NaN%) 0.00s 0.00s
Total Time 19.48s (± 0.35%) 19.57s (± 0.66%) +0.09s (+ 0.44%) 19.33s 19.88s
System
Machine Namets-ci-ubuntu
Platformlinux 4.4.0-166-generic
Architecturex64
Available Memory16 GB
Available Memory1 GB
CPUs4 × Intel(R) Core(TM) i7-4770 CPU @ 3.40GHz
Hosts
  • node (v10.16.3, x64)
  • node (v12.1.0, x64)
  • node (v8.9.0, x64)
  • node (v8.9.0, x86)
Scenarios
  • Angular - node (v10.16.3, x64)
  • Angular - node (v12.1.0, x64)
  • Angular - node (v8.9.0, x64)
  • Angular - node (v8.9.0, x86)
  • Monaco - node (v10.16.3, x64)
  • Monaco - node (v12.1.0, x64)
  • Monaco - node (v8.9.0, x64)
  • Monaco - node (v8.9.0, x86)
  • TFS - node (v10.16.3, x64)
  • TFS - node (v12.1.0, x64)
  • TFS - node (v8.9.0, x64)
  • TFS - node (v8.9.0, x86)
  • material-ui - node (v10.16.3, x64)
  • material-ui - node (v12.1.0, x64)
  • material-ui - node (v8.9.0, x64)
  • material-ui - node (v8.9.0, x86)
Benchmark Name Iterations
Current 40558 10
Baseline master 10

@rbuckton rbuckton merged commit dba042d into master Sep 15, 2020
@rbuckton rbuckton deleted the addVoidToPromiseCodeFix branch September 17, 2020 01:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Author: Team For Uncommitted Bug PR for untriaged, rejected, closed or missing bug
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants