feat(oxlint): support allow option for prefer-promise-reject-errors (#20934)

Add the TypeScript rule config surface for the new `allow` specifier option and extend the oxlint CLI fixture to verify the option end to end.

Tested with:
- `cargo test -p oxc_linter prefer_promise_reject_errors -- --nocapture`
- `cargo test -p oxlint test_tsgolint_rule_options -- --nocapture`
- `node --run fmt`

AI usage disclosure: Codex was used to implement the config support, add the CLI fixture coverage, and prepare this PR. The changes were reviewed in the local workspace before submission.
This commit is contained in:
camc314 committed 2026-04-01 14:50:20 +00:00
1 parent a04f405cbe
commit 0b84de1c1a
4 files changed
+85 -51

No files matched your search

@@ -32,6 +32,16 @@
]
}
],
"typescript/prefer-promise-reject-errors": [
"error",
{
"allow": [
{ "from": "file", "name": "AllowedRejection" }
],
"allowThrowingAny": false,
"allowThrowingUnknown": false
}
],
"typescript/no-unnecessary-type-assertion": [
"error",
{
@@ -46,6 +46,16 @@ const allowedSpread = { ...allowedFunc };
// This SHOULD error because notAllowedFunc is NOT in the allow list
const notAllowedSpread = { ...notAllowedFunc };
// Test prefer-promise-reject-errors with allow option
class AllowedRejection {}
class NotAllowedRejection {}
// This should NOT error because AllowedRejection is in the allow list
Promise.reject(new AllowedRejection());
// This SHOULD error because NotAllowedRejection is NOT in the allow list
Promise.reject(new NotAllowedRejection());
// Test no-unnecessary-type-assertion with checkLiteralConstAssertions option
// When checkLiteralConstAssertions is true, this SHOULD error
const literalConst = 'hello' as const;
@@ -23,97 +23,105 @@ working directory: fixtures/cli/tsgolint_rule_options
`----
help: Did you forget to call the function?
x typescript-eslint(prefer-promise-reject-errors): Expected the Promise rejection reason to be an Error.
,-[test.ts:57:1]
56 | // This SHOULD error because NotAllowedRejection is NOT in the allow list
57 | Promise.reject(new NotAllowedRejection());
: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
58 |
`----
x typescript-eslint(no-unsafe-member-access): Unsafe member access .bar on an `any` value.
,-[test.ts:58:31]
57 | // This SHOULD error because it's not using optional chaining
58 | const unsafeAccess = anyValue.bar;
,-[test.ts:68:31]
67 | // This SHOULD error because it's not using optional chaining
68 | const unsafeAccess = anyValue.bar;
: ^^^
59 |
69 |
`----
x typescript-eslint(no-base-to-string): 'unknownValue' may use Object's default stringification format ('[object Object]') when stringified.
,-[test.ts:63:20]
62 | // This SHOULD error because checkUnknown is true
63 | const unknownStr = unknownValue.toString();
,-[test.ts:73:20]
72 | // This SHOULD error because checkUnknown is true
73 | const unknownStr = unknownValue.toString();
: ^^^^^^^^^^^^
64 |
74 |
`----
help: Consider picking a property (e.g. `user.name`), using a formatter (or `JSON.stringify`), or implementing a custom `toString()`/`toLocaleString()` on the type.
x typescript-eslint(no-unnecessary-condition): Unnecessary conditional, value is always truthy.
,-[test.ts:68:5]
67 | // This SHOULD error because this object is always truthy
68 | if (alwaysTruthyObject) {
,-[test.ts:78:5]
77 | // This SHOULD error because this object is always truthy
78 | if (alwaysTruthyObject) {
: ^^^^^^^^^^^^^^^^^^
69 | result += 1;
79 | result += 1;
`----
x typescript-eslint(consistent-type-exports): Type export ExportOnlyType is not a value and should be exported using `export type`.
,-[test.ts:81:1]
80 | // This SHOULD error because ExportOnlyType is only used as a type.
81 | export { ExportOnlyType, exportOnlyValue };
,-[test.ts:91:1]
90 | // This SHOULD error because ExportOnlyType is only used as a type.
91 | export { ExportOnlyType, exportOnlyValue };
: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
82 |
92 |
`----
x typescript-eslint(strict-void-return): Value returned in a context where a void return is expected.
,-[test.ts:89:25]
88 | // This SHOULD error because returning string is not allowed in a void callback
89 | takesVoidCallback(() => 'not-void');
: ^^^^^^^^^^
90 |
`----
,-[test.ts:99:25]
98 | // This SHOULD error because returning string is not allowed in a void callback
99 | takesVoidCallback(() => 'not-void');
: ^^^^^^^^^^
100 |
`----
x typescript-eslint(prefer-readonly-parameter-types): Parameter should be a readonly type.
,-[test.ts:92:27]
91 | // Test prefer-readonly-parameter-types options
92 | function takesAllowedType(input: RegExp): void {
: ^^^^^^^^^^^^^
93 | console.log(input.source);
`----
,-[test.ts:102:27]
101 | // Test prefer-readonly-parameter-types options
102 | function takesAllowedType(input: RegExp): void {
: ^^^^^^^^^^^^^
103 | console.log(input.source);
`----
x typescript-eslint(prefer-readonly-parameter-types): Parameter should be a readonly type.
,-[test.ts:101:32]
100 | // This SHOULD error because parameter type is mutable
101 | function takesMutableParameter(input: MutableParameter): void {
,-[test.ts:111:32]
110 | // This SHOULD error because parameter type is mutable
111 | function takesMutableParameter(input: MutableParameter): void {
: ^^^^^^^^^^^^^^^^^^^^^^^
102 | console.log(input.value);
112 | console.log(input.value);
`----
x typescript-eslint(prefer-readonly): Member 'handler' is never reassigned; mark it as `readonly`.
,-[test.ts:107:3]
106 | class PreferReadonlyOptionExample {
107 | private handler = () => 1;
,-[test.ts:117:3]
116 | class PreferReadonlyOptionExample {
117 | private handler = () => 1;
: ^^^^^^^^^^^^^^^
108 | getValue() {
118 | getValue() {
`----
x typescript-eslint(prefer-string-starts-ends-with): Use 'String#startsWith' method instead.
,-[test.ts:118:28]
117 | // This SHOULD error because startsWith is preferred here
118 | const boundarySliceMatch = boundaryText.slice(0, 3) === 'foo';
,-[test.ts:128:28]
127 | // This SHOULD error because startsWith is preferred here
128 | const boundarySliceMatch = boundaryText.slice(0, 3) === 'foo';
: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
119 |
129 |
`----
x typescript-eslint(consistent-return): Function 'maybeReturnValue' expected a return value.
,-[test.ts:126:3]
125 | // This SHOULD be treated as an unspecified return with the option enabled
126 | return undefined;
,-[test.ts:136:3]
135 | // This SHOULD be treated as an unspecified return with the option enabled
136 | return undefined;
: ^^^^^^^^^^^^^^^^^
127 | }
137 | }
`----
x typescript-eslint(dot-notation): ["name"] is better written in dot notation.
,-[test.ts:145:19]
144 | // This SHOULD error because static property access should use dot notation
145 | dotNotationSimple['name'];
,-[test.ts:155:19]
154 | // This SHOULD error because static property access should use dot notation
155 | dotNotationSimple['name'];
: ^^^^^^
146 |
156 |
`----
Found 0 warnings and 13 errors.
Finished in <variable>ms on 1 file with 15 rules using 1 threads.
Found 0 warnings and 14 errors.
Finished in <variable>ms on 1 file with 16 rules using 1 threads.
----------
CLI result: LintFoundErrors
----------
@@ -2,7 +2,10 @@ use oxc_macros::declare_oxc_lint;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use crate::rule::{DefaultRuleConfig, Rule};
use crate::{
rule::{DefaultRuleConfig, Rule},
utils::TypeOrValueSpecifier,
};
#[derive(Debug, Default, Clone, Deserialize)]
pub struct PreferPromiseRejectErrors(Box<PreferPromiseRejectErrorsConfig>);
@@ -10,6 +13,9 @@ pub struct PreferPromiseRejectErrors(Box<PreferPromiseRejectErrorsConfig>);
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, Default)]
#[serde(rename_all = "camelCase", default, deny_unknown_fields)]
pub struct PreferPromiseRejectErrorsConfig {
/// An array of type or value specifiers for additional types that are allowed
/// as Promise rejection reasons.
pub allow: Vec<TypeOrValueSpecifier>,
/// Whether to allow calling `Promise.reject()` with no arguments.
pub allow_empty_reject: bool,
/// Whether to allow rejecting Promises with values typed as `any`.