-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
feat(eslint-plugin): add rule use-unknown-in-catch-callback-variables
#8383
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
JoshuaKGoldberg
merged 28 commits into
typescript-eslint:main
from
kirkwaiblinger:unknown-in-catch-variables-typed
Mar 17, 2024
Merged
Changes from all commits
Commits
Show all changes
28 commits
Select commit
Hold shift + click to select a range
cae9abd
Add rule use-unknown-in-catch-callback-variables
kirkwaiblinger dff8954
delete PR comment reminders
kirkwaiblinger 2bbf5fc
address code coverage gaps
kirkwaiblinger 955e113
fix spell checker
kirkwaiblinger ccb352d
fix broken link
kirkwaiblinger 2364a18
little bit of simplifying
kirkwaiblinger 2fcf80c
simplify arrow function parentheses check
kirkwaiblinger f3cd4d4
Your -> The
kirkwaiblinger 9ddaeb0
Clean up some comments and simplify a bit
kirkwaiblinger 8937352
add to strict config
kirkwaiblinger f9af052
Add examples to docs
kirkwaiblinger cfd1403
Improve handling of destructuring parameters
kirkwaiblinger 1112bfb
tweaks
kirkwaiblinger 74b4096
docs changes
kirkwaiblinger c3db638
Improve wording for "when not to use this"
kirkwaiblinger 2f229ab
improve wording and fix mistake
kirkwaiblinger 88176a3
remove dead return
kirkwaiblinger 2a38257
address istanbul-ignore
kirkwaiblinger e124d90
add bizarre syn
8000
tax test cases
kirkwaiblinger 671c44d
Improve main error message wording
kirkwaiblinger 3263652
utility for rest parameter
kirkwaiblinger 1f5f70e
tweaks
kirkwaiblinger 2f175df
fix casing
kirkwaiblinger b721623
restore older versions of configs
kirkwaiblinger 91a7725
config update
kirkwaiblinger ef47613
fix internal violations
kirkwaiblinger 4d1860f
Merge branch 'main'
JoshuaKGoldberg 89bed41
Regenerate configs
JoshuaKGoldberg File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
80 changes: 80 additions & 0 deletions
80
packages/eslint-plugin/docs/rules/use-unknown-in-catch-callback-variable.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
--- | ||
description: 'Enforce typing arguments in `.catch()` callbacks as `unknown`.' | ||
--- | ||
|
||
> 🛑 This file is source code, not the primary documentation location! 🛑 | ||
> | ||
> See **https://typescript-eslint.io/rules/use-unknown-in-catch-callback-variable** for documentation. | ||
|
||
This rule enforces that you always use the `unknown` type for the parameter of a `Promise.prototype.catch()` callback. | ||
|
||
<!--tabs--> | ||
|
||
### ❌ Incorrect | ||
|
||
```ts | ||
Promise.reject(new Error('I will reject!')).catch(err => { | ||
console.log(err); | ||
}); | ||
|
||
Promise.reject(new Error('I will reject!')).catch((err: any) => { | ||
console.log(err); | ||
}); | ||
|
||
Promise.reject(new Error('I will reject!')).catch((err: Error) => { | ||
console.log(err); | ||
}); | ||
``` | ||
|
||
### ✅ Correct | ||
|
||
```ts | ||
Promise.reject(new Error('I will reject!')).catch((err: unknown) => { | ||
console.log(err); | ||
}); | ||
``` | ||
|
||
<!--/tabs--> | ||
|
||
The reason for this rule is to enable programmers to impose constraints on `Promise` error handling analogously to what TypeScript provides for ordinary exception handling. | ||
|
||
For ordinary exceptions, TypeScript treats the `catch` variable as `any` by default. However, `unknown` would be a more accurate type, so TypeScript [introduced the `useUnknownInCatchVariables` compiler option](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-4.html#defaulting-to-the-unknown-type-in-catch-variables---useunknownincatchvariables) to treat the `catch` variable as `unknown` instead. | ||
|
||
```ts | ||
try { | ||
throw x; | ||
} catch (err) { | ||
// err has type 'any' with useUnknownInCatchVariables: false | ||
// err has type 'unknown' with useUnknownInCatchVariables: true | ||
} | ||
``` | ||
|
||
The Promise analog of the `try-catch` block, [`Promise.prototype.catch()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/catch), is not affected by the `useUnknownInCatchVariables` compiler option, and its "`catch` variable" will always have the type `any`. | ||
|
||
```ts | ||
Promise.reject(x).catch(err => { | ||
// err has type 'any' regardless of `useUnknownInCatchVariables` | ||
}); | ||
``` | ||
|
||
However, you can still provide an explicit type annotation, which lets you achieve the same effect as the `useUnknownInCatchVariables` option does for synchronous `catch` variables. | ||
|
||
```ts | ||
Promise.reject(x).catch((err: unknown) => { | ||
// err has type 'unknown' | ||
}); | ||
``` | ||
|
||
:::info | ||
There is actually a way to have the `catch()` callback variable use the `unknown` type _without_ an explicit type annotation at the call sites, but it has the drawback that it involves overriding global type declarations. | ||
For example, the library [better-TypeScript-lib](https://github.com/uhyo/better-typescript-lib) sets this up globally for your project (see [the relevant lines in the better-TypeScript-lib source code](https://github.com/uhyo/better-typescript-lib/blob/c294e177d1cc2b1d1803febf8192a4c83a1fe028/lib/lib.es5.d.ts#L635) for details on how). | ||
|
||
For further reading on this, you may also want to look into | ||
[the discussion in the proposal for this rule](https://github.com/typescript-eslint/typescript-eslint/issues/7526#issuecomment-1690600813) and [this TypeScript issue on typing catch callback variables as unknown](https://github.com/microsoft/TypeScript/issues/45602). | ||
::: | ||
|
||
## When Not To Use It | ||
|
||
If your codebase is not yet able to enable `useUnknownInCatchVariables`, it likely would be similarly difficult to enable this rule. | ||
|
||
If you have modified the global type declarations in order to make `catch()` callbacks use the `unknown` type without an explicit type annotation, you do not need this rule. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just double checking/confirming we want to add this to the strict config before the next major?
i don't think we've ever expressly discussed our strategy for the strict configs.
they're not technically included in our versioning policy for a major release if they're changed - but I wonder if adding a brand new rule in a minor might be a bit disruptive for users?
cc @JoshuaKGoldberg
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup confirmed, we've done this before (#7957, #8011). Since the strict config is explicitly out of semver, users are opting into this.