feat(linter/typescript): implement no-unsafe-enum-assignment rule (#28117)

Register `typescript/no-unsafe-enum-assignment` as a type-aware rule backed by oxlint-tsgolint 7.0.2005, following https://github.com/oxc-project/tsgolint/pull/1316. Add rule documentation and regenerate the rule registry, JSON schema, and TypeScript config types.

Extend the existing type-aware CLI fixture with unsafe assignments, arguments, returns, assertions, mutations, and enum-key access, plus valid enum-member and bitwise-combination cases. Reuse the existing tests and update their diagnostic and count snapshots.
This commit is contained in:
camc314 committed 2026-10-23 01:29:21 +00:00
1 parent 165c91f9c1
commit d1b00f45ec
13 files changed
+762 -593

No files matched your search

@@ -32,6 +32,7 @@
"typescript/no-unsafe-argument": "error",
"typescript/no-unsafe-assignment": "error",
"typescript/no-unsafe-call": "error",
"typescript/no-unsafe-enum-assignment": "error",
"typescript/no-unsafe-enum-comparison": "error",
"typescript/no-unsafe-member-access": "error",
"typescript/no-unsafe-return": "error",
@@ -0,0 +1,24 @@
enum Fruit {
Apple,
Banana,
}
const fruit: Fruit = 1;
function takesFruit(value: Fruit): void {}
takesFruit(1);
function getFruit(): Fruit {
return 0;
}
const asserted = 1 as Fruit;
let mutable = Fruit.Apple;
mutable++;
declare const names: { [key in Fruit]: string };
names[0];
// Enum members and bitwise combinations are allowed.
const safeFruit: Fruit = Fruit.Apple;
takesFruit(Fruit.Banana);
const combined: Fruit = Fruit.Apple | Fruit.Banana;
names[Fruit.Apple];
export {};
@@ -1490,6 +1490,7 @@ export interface DummyRuleMap {
"typescript/no-unsafe-assignment"?: RuleNoConfig;
"typescript/no-unsafe-call"?: RuleNoConfig;
"typescript/no-unsafe-declaration-merging"?: RuleNoConfig;
"typescript/no-unsafe-enum-assignment"?: RuleNoConfig;
"typescript/no-unsafe-enum-comparison"?: RuleNoConfig;
"typescript/no-unsafe-function-type"?: RuleNoConfig;
"typescript/no-unsafe-member-access"?: RuleNoConfig | [AllowWarnDeny, NoUnsafeMemberAccessConfig];
@@ -6,8 +6,8 @@ arguments: --type-aware --silent
working directory: fixtures/cli/tsgolint
----------
Found 0 warnings and 86 errors.
Finished in <variable>ms on 61 files with 60 rules using 1 threads.
Found 0 warnings and 92 errors.
Finished in <variable>ms on 62 files with 61 rules using 1 threads.
----------
CLI result: LintFoundErrors
----------
@@ -42,7 +42,7 @@ working directory: fixtures/cli/tsgolint
help: Remove the debugger statement
Found 2 warnings and 2 errors.
Finished in <variable>ms on 61 files with 2 rules using 1 threads.
Finished in <variable>ms on 62 files with 2 rules using 1 threads.
----------
CLI result: LintFoundErrors
----------
@@ -16,7 +16,7 @@ working directory: fixtures/cli/tsgolint
help: Remove the debugger statement
Found 0 warnings and 1 error.
Finished in <variable>ms on 1 file with 60 rules using 1 threads.
Finished in <variable>ms on 1 file with 61 rules using 1 threads.
----------
CLI result: LintFoundErrors
----------
@@ -352,6 +352,54 @@ working directory: fixtures/cli/tsgolint
3 |
`----
x typescript(no-unsafe-enum-assignment): The value assigned here does not have a shared enum type with the expected enum 'Fruit'.
,-[no-unsafe-enum-assignment.ts:6:7]
5 |
6 | const fruit: Fruit = 1;
: ^^^^^^^^^^^^^^^^
7 | function takesFruit(value: Fruit): void {}
`----
x typescript(no-unsafe-enum-assignment): The argument passed here does not have a shared enum type with the expected enum 'Fruit'.
,-[no-unsafe-enum-assignment.ts:8:12]
7 | function takesFruit(value: Fruit): void {}
8 | takesFruit(1);
: ^
9 | function getFruit(): Fruit {
`----
x typescript(no-unsafe-enum-assignment): The value returned here does not have a shared enum type with the expected enum 'Fruit'.
,-[no-unsafe-enum-assignment.ts:10:3]
9 | function getFruit(): Fruit {
10 | return 0;
: ^^^^^^^^^
11 | }
`----
x typescript(no-unsafe-enum-assignment): The value asserted here does not have a shared enum type with the expected enum 'Fruit'.
,-[no-unsafe-enum-assignment.ts:12:18]
11 | }
12 | const asserted = 1 as Fruit;
: ^^^^^^^^^^
13 | let mutable = Fruit.Apple;
`----
x typescript(no-unsafe-enum-assignment): This mutation can produce a value outside of the expected enum 'Fruit'.
,-[no-unsafe-enum-assignment.ts:14:1]
13 | let mutable = Fruit.Apple;
14 | mutable++;
: ^^^^^^^^^
15 | declare const names: { [key in Fruit]: string };
`----
x typescript(no-unsafe-enum-assignment): The computed key used here does not have a shared enum type with the expected enum 'Fruit'.
,-[no-unsafe-enum-assignment.ts:16:7]
15 | declare const names: { [key in Fruit]: string };
16 | names[0];
: ^
17 |
`----
x typescript(no-unsafe-enum-comparison): The two values in this comparison do not have a shared enum type.
,-[no-unsafe-enum-comparison.ts:9:20]
8 | }
@@ -720,8 +768,8 @@ working directory: fixtures/cli/tsgolint
`----
help: If your function does not access `this`, you can annotate it with `this: void`, or consider using an arrow function instead.
Found 0 warnings and 86 errors.
Finished in <variable>ms on 61 files with 60 rules using 1 threads.
Found 0 warnings and 92 errors.
Finished in <variable>ms on 62 files with 61 rules using 1 threads.
----------
CLI result: LintFoundErrors
----------
@@ -42,7 +42,7 @@ working directory: fixtures/cli/tsgolint
help: Remove the debugger statement
Found 2 warnings and 2 errors.
Finished in <variable>ms on 61 files with 2 rules using 1 threads.
Finished in <variable>ms on 62 files with 2 rules using 1 threads.
----------
CLI result: LintFoundErrors
----------
+5
View File
@@ -1972,6 +1972,11 @@ impl RuleRunner
const RUN_FUNCTIONS: RuleRunFunctionsImplemented = RuleRunFunctionsImplemented::Run;
}
impl RuleRunner for crate::rules::typescript::no_unsafe_enum_assignment::NoUnsafeEnumAssignment {
const NODE_TYPES: Option<&AstTypesBitset> = None;
const RUN_FUNCTIONS: RuleRunFunctionsImplemented = RuleRunFunctionsImplemented::Unknown;
}
impl RuleRunner for crate::rules::typescript::no_unsafe_enum_comparison::NoUnsafeEnumComparison {
const NODE_TYPES: Option<&AstTypesBitset> = None;
const RUN_FUNCTIONS: RuleRunFunctionsImplemented = RuleRunFunctionsImplemented::Unknown;
File diff suppressed because it is too large. Load diff
+1
View File
@@ -299,6 +299,7 @@ pub(crate) mod typescript {
pub mod no_unsafe_assignment;
pub mod no_unsafe_call;
pub mod no_unsafe_declaration_merging;
pub mod no_unsafe_enum_assignment;
pub mod no_unsafe_enum_comparison;
pub mod no_unsafe_function_type;
pub mod no_unsafe_member_access;
@@ -0,0 +1,61 @@
use oxc_macros::declare_oxc_lint;
use crate::rule::Rule;
#[derive(Debug, Default, Clone)]
pub struct NoUnsafeEnumAssignment;
declare_oxc_lint!(
/// ### What it does
///
/// Disallows using values that do not share an enum type where an enum value is expected.
/// This includes assignments, arguments, return values, type assertions, mutations,
/// and computed keys in objects keyed by enum members.
///
/// ### Why is this bad?
///
/// Using the underlying number or string instead of an enum member bypasses the
/// enum's type safety. Numeric mutations can also produce values outside the enum.
/// Referencing enum members keeps values tied to the enum when its members change.
///
/// ### Examples
///
/// Examples of **incorrect** code for this rule:
/// ```ts
/// enum Fruit {
/// Apple,
/// Banana,
/// }
///
/// const fruit: Fruit = 0;
/// function eat(fruit: Fruit) {}
/// eat(1);
///
/// function getFruit(): Fruit {
/// return 0;
/// }
/// ```
///
/// Examples of **correct** code for this rule:
/// ```ts
/// enum Fruit {
/// Apple,
/// Banana,
/// }
///
/// const fruit: Fruit = Fruit.Apple;
/// function eat(fruit: Fruit) {}
/// eat(Fruit.Banana);
///
/// function getFruit(): Fruit {
/// return Fruit.Apple;
/// }
/// ```
NoUnsafeEnumAssignment(tsgolint),
typescript,
suspicious,
version = "next",
short_description = "Disallow unsafe enum assignments.",
);
impl Rule for NoUnsafeEnumAssignment {}
+3
View File
@@ -8371,6 +8371,9 @@
"typescript/no-unsafe-declaration-merging": {
"$ref": "#/definitions/RuleNoConfig"
},
"typescript/no-unsafe-enum-assignment": {
"$ref": "#/definitions/RuleNoConfig"
},
"typescript/no-unsafe-enum-comparison": {
"$ref": "#/definitions/RuleNoConfig"
},