Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/oxlint/fixtures/tsgolint/.oxlintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
"typescript/prefer-nullish-coalescing": "error",
"typescript/prefer-optional-chain": "error",
"typescript/prefer-promise-reject-errors": "error",
"typescript/prefer-readonly": "error",
"typescript/prefer-readonly-parameter-types": "error",
"typescript/prefer-reduce-type-parameter": "error",
"typescript/prefer-return-this-type": "error",
Expand Down
12 changes: 12 additions & 0 deletions apps/oxlint/fixtures/tsgolint/prefer-readonly.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
class Example {
private value = 1;
private readonly ok = 2;

constructor() {
this.value = 2;
}

mutate() {
return this.ok;
}
}
6 changes: 6 additions & 0 deletions apps/oxlint/fixtures/tsgolint_rule_options/.oxlintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,12 @@
"treatMethodsAsReadonly": true
}
],
"typescript/prefer-readonly": [
"error",
{
"onlyInlineLambdas": true
}
],
"typescript/only-throw-error": [
"error",
{
Expand Down
8 changes: 8 additions & 0 deletions apps/oxlint/fixtures/tsgolint_rule_options/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,14 @@ function takesMutableParameter(input: MutableParameter): void {
console.log(input.value);
}

// Test prefer-readonly with onlyInlineLambdas option
class PreferReadonlyOptionExample {
private handler = () => 1;
getValue() {
return this.handler();
}
}

// Test only-throw-error with allowRethrowing option
// When allowRethrowing is false, rethrowing a caught error SHOULD error
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ arguments: --type-aware --silent
working directory: fixtures/tsgolint
----------

Found 0 warnings and 63 errors.
Finished in <variable>ms on 52 files with 51 rules using 1 threads.
Found 0 warnings and 64 errors.
Finished in <variable>ms on 53 files with 52 rules using 1 threads.
----------
CLI result: LintFoundErrors
----------
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ working directory: fixtures/tsgolint
help: Remove the debugger statement

Found 2 warnings and 2 errors.
Finished in <variable>ms on 52 files with 1 rules using 1 threads.
Finished in <variable>ms on 53 files with 1 rules using 1 threads.
----------
CLI result: LintFoundErrors
----------
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ working directory: fixtures/tsgolint
help: Remove the debugger statement

Found 0 warnings and 1 error.
Finished in <variable>ms on 1 file with 51 rules using 1 threads.
Finished in <variable>ms on 1 file with 52 rules using 1 threads.
----------
CLI result: LintFoundErrors
----------
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,14 @@ working directory: fixtures/tsgolint
6 | input.value = input.value.trim();
`----

x typescript-eslint(prefer-readonly): Member 'value' is never reassigned; mark it as `readonly`.
,-[prefer-readonly.ts:2:3]
1 | class Example {
2 | private value = 1;
: ^^^^^^^^^^^^^
3 | private readonly ok = 2;
`----

x typescript-eslint(no-unnecessary-type-assertion): This assertion is unnecessary since it does not change the type of the expression.
,-[prefer-reduce-type-parameter.ts:2:13]
1 | const numbers = [1, 2, 3];
Expand Down Expand Up @@ -494,8 +502,8 @@ working directory: fixtures/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 63 errors.
Finished in <variable>ms on 52 files with 51 rules using 1 threads.
Found 0 warnings and 64 errors.
Finished in <variable>ms on 53 files with 52 rules using 1 threads.
----------
CLI result: LintFoundErrors
----------
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,16 @@ working directory: fixtures/tsgolint_rule_options
102 | console.log(input.value);
`----

Found 0 warnings and 9 errors.
Finished in <variable>ms on 1 file with 11 rules using 1 threads.
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;
: ^^^^^^^^^^^^^^^
108 | getValue() {
`----

Found 0 warnings and 10 errors.
Finished in <variable>ms on 1 file with 12 rules using 1 threads.
----------
CLI result: LintFoundErrors
----------
5 changes: 5 additions & 0 deletions crates/oxc_linter/src/generated/rule_runner_impls.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

27 changes: 25 additions & 2 deletions crates/oxc_linter/src/generated/rules_enum.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/oxc_linter/src/rules.rs
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,7 @@ pub(crate) mod typescript {
pub mod prefer_nullish_coalescing;
pub mod prefer_optional_chain;
pub mod prefer_promise_reject_errors;
pub mod prefer_readonly;
pub mod prefer_readonly_parameter_types;
pub mod prefer_reduce_type_parameter;
pub mod prefer_return_this_type;
Expand Down
64 changes: 64 additions & 0 deletions crates/oxc_linter/src/rules/typescript/prefer_readonly.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
use oxc_macros::declare_oxc_lint;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

use crate::rule::{DefaultRuleConfig, Rule};

#[derive(Debug, Default, Clone, Deserialize)]
pub struct PreferReadonly(Box<PreferReadonlyConfig>);

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, Default)]
#[serde(rename_all = "camelCase", default, deny_unknown_fields)]
pub struct PreferReadonlyConfig {
/// Restrict checks to members immediately initialized with inline lambda values.
pub only_inline_lambdas: bool,
}

declare_oxc_lint!(
/// ### What it does
///
/// Require class members that are never reassigned to be marked `readonly`.
///
/// ### Why is this bad?
///
/// Members that never change should be declared `readonly` to make class invariants explicit
/// and prevent accidental mutation.
///
/// ### Examples
///
/// Examples of **incorrect** code for this rule:
/// ```ts
/// class Counter {
/// private value = 0;
///
/// getValue() {
/// return this.value;
/// }
/// }
/// ```
///
/// Examples of **correct** code for this rule:
/// ```ts
/// class Counter {
/// private readonly value = 0;
///
/// getValue() {
/// return this.value;
/// }
/// }
/// ```
PreferReadonly(tsgolint),
typescript,
nursery,
config = PreferReadonlyConfig,
);

impl Rule for PreferReadonly {
fn from_configuration(value: serde_json::Value) -> Result<Self, serde_json::error::Error> {
serde_json::from_value::<DefaultRuleConfig<Self>>(value).map(DefaultRuleConfig::into_inner)
}

fn to_configuration(&self) -> Option<Result<serde_json::Value, serde_json::Error>> {
Some(serde_json::to_value(&*self.0))
}
}
Loading