Skip to content
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

feat(eslint-plugin): add rule use-unknown-in-catch-callback-variables #8383

Merged
Show file tree
Hide file tree
Changes from 5 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 Feb 5, 2024
dff8954
delete PR comment reminders
kirkwaiblinger Feb 5, 2024
2bbf5fc
address code coverage gaps
kirkwaiblinger Feb 6, 2024
955e113
fix spell checker
kirkwaiblinger Feb 6, 2024
ccb352d
fix broken link
kirkwaiblinger Feb 6, 2024
2364a18
little bit of simplifying
kirkwaiblinger Feb 8, 2024
2fcf80c
simplify arrow function parentheses check
kirkwaiblinger Feb 8, 2024
f3cd4d4
Your -> The
kirkwaiblinger Feb 8, 2024
9ddaeb0
Clean up some comments and simplify a bit
kirkwaiblinger Feb 8, 2024
8937352
add to strict config
kirkwaiblinger Feb 8, 2024
f9af052
Add examples to docs
kirkwaiblinger Feb 8, 2024
cfd1403
Improve handling of destructuring parameters
kirkwaiblinger Feb 8, 2024
1112bfb
tweaks
kirkwaiblinger Feb 8, 2024
74b4096
docs changes
kirkwaiblinger Feb 8, 2024
c3db638
Improve wording for "when not to use this"
kirkwaiblinger Feb 8, 2024
2f229ab
improve wording and fix mistake
kirkwaiblinger Feb 8, 2024
88176a3
remove dead return
kirkwaiblinger Feb 8, 2024
2a38257
address istanbul-ignore
kirkwaiblinger Feb 8, 2024
e124d90
add bizarre syntax test cases
kirkwaiblinger Feb 8, 2024
671c44d
Improve main error message wording
kirkwaiblinger Feb 8, 2024
3263652
utility for rest parameter
kirkwaiblinger Feb 9, 2024
1f5f70e
tweaks
kirkwaiblinger Feb 9, 2024
2f175df
fix casing
kirkwaiblinger Feb 23, 2024
b721623
restore older versions of configs
kirkwaiblinger Feb 23, 2024
91a7725
config update
kirkwaiblinger Feb 23, 2024
ef47613
fix internal violations
kirkwaiblinger Feb 23, 2024
4d1860f
Merge branch 'main'
JoshuaKGoldberg Mar 17, 2024
89bed41
Regenerate configs
JoshuaKGoldberg Mar 17, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
---
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.

In the context of exception handling, 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.
JoshuaKGoldberg marked this conversation as resolved.
Show resolved Hide resolved

```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'
});
```

This rule enforces that you always provide the `unknown` type annotation.

<!--tabs-->

### ❌ Incorrect
kirkwaiblinger marked this conversation as resolved.
Show resolved Hide resolved

```ts
Promise.reject(new Error('I will reject!')).catch(err => {
console.log(err);
});
```

### ✅ Correct

```ts
Promise.reject(new Error('I will reject!')).catch((err: unknown) => {
console.log(err);
});
```

<!--/tabs-->

:::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. See [this comment](https://github.com/typescript-eslint/typescript-eslint/issues/7526#issuecomment-1690600813) on the proposal for this rule, and [this TypeScript issue](https://github.com/microsoft/TypeScript/issues/45602#issuecomment-934427206).
JoshuaKGoldberg marked this conversation as resolved.
Show resolved Hide resolved
JoshuaKGoldberg marked this conversation as resolved.
Show resolved Hide resolved
:::

## When Not To Use It

If your codebase does not use `useUnknownInCatchVariables`, it probably does not make sense to enable this rule, though it wouldn't have any adverse affects to do so. Alternately, 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.
kirkwaiblinger marked this conversation as resolved.
Show resolved Hide resolved
1 change: 1 addition & 0 deletions packages/eslint-plugin/src/configs/all.ts
JoshuaKGoldberg marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -153,5 +153,6 @@ export = {
'@typescript-eslint/typedef': 'error',
'@typescript-eslint/unbound-method': 'error',
'@typescript-eslint/unified-signatures': 'error',
'@typescript-eslint/use-unknown-in-catch-callback-variable': 'error',
},
} satisfies ClassicConfig.Config;
1 change: 1 addition & 0 deletions packages/eslint-plugin/src/configs/disable-type-checked.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,5 +61,6 @@ export = {
'@typescript-eslint/strict-boolean-expressions': 'off',
'@typescript-eslint/switch-exhaustiveness-check': 'off',
'@typescript-eslint/unbound-method': 'off',
'@typescript-eslint/use-unknown-in-catch-callback-variable': 'off',
},
} satisfies ClassicConfig.Config;
2 changes: 2 additions & 0 deletions packages/eslint-plugin/src/rules/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ import typeAnnotationSpacing from './type-annotation-spacing';
import typedef from './typedef';
import unboundMethod from './unbound-method';
import unifiedSignatures from './unified-signatures';
import useUnknownInCatchCallbackVariable from './use-unknown-in-catch-callback-variable';

export default {
'adjacent-overload-signatures': adjacentOverloadSignatures,
Expand Down Expand Up @@ -282,4 +283,5 @@ export default {
typedef: typedef,
'unbound-method': unboundMethod,
'unified-signatures': unifiedSignatures,
'use-unknown-in-catch-callback-variable': useUnknownInCatchCallbackVariable,
} satisfies Linter.PluginRules;