Skip to content

Commit 0682abe

Browse files
committed
feat(linter): expose type errors via tsgolint
1 parent d29da4c commit 0682abe

File tree

10 files changed

+145
-1
lines changed

10 files changed

+145
-1
lines changed
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
{
2+
"categories": {
3+
"correctness": "off"
4+
},
5+
"rules": {
6+
"typescript/await-thenable": "error",
7+
"typescript/no-array-delete": "error",
8+
"typescript/no-base-to-string": "error",
9+
"typescript/no-confusing-void-expression": "error",
10+
"typescript/no-deprecated": "error",
11+
"typescript/no-duplicate-type-constituents": "error",
12+
"typescript/no-floating-promises": "error",
13+
"typescript/no-for-in-array": "error",
14+
"typescript/no-implied-eval": "error",
15+
"typescript/no-meaningless-void-operator": "error",
16+
"typescript/no-misused-spread": "error",
17+
"typescript/no-mixed-enums": "error",
18+
"typescript/no-redundant-type-constituents": "error",
19+
"typescript/no-unnecessary-boolean-literal-compare": "error",
20+
"typescript/no-unnecessary-template-expression": "error",
21+
"typescript/no-unnecessary-type-arguments": "error",
22+
"typescript/no-unnecessary-type-assertion": "error",
23+
"typescript/no-unsafe-argument": "error",
24+
"typescript/no-unsafe-assignment": "error",
25+
"typescript/no-unsafe-call": "error",
26+
"typescript/no-unsafe-enum-comparison": "error",
27+
"typescript/no-unsafe-member-access": "error",
28+
"typescript/no-unsafe-return": "error",
29+
"typescript/no-unsafe-type-assertion": "error",
30+
"typescript/no-unsafe-unary-minus": "error",
31+
"typescript/non-nullable-type-assertion-style": "error",
32+
"typescript/only-throw-error": "error",
33+
"typescript/prefer-includes": "error",
34+
"typescript/prefer-promise-reject-errors": "error",
35+
"typescript/prefer-reduce-type-parameter": "error",
36+
"typescript/prefer-return-this-type": "error",
37+
"typescript/promise-function-async": "error",
38+
"typescript/related-getter-setter-pairs": "error",
39+
"typescript/require-array-sort-compare": "error",
40+
"typescript/require-await": "error",
41+
"typescript/restrict-plus-operands": "error",
42+
"typescript/restrict-template-expressions": "error",
43+
"typescript/return-await": "error",
44+
"typescript/strict-boolean-expressions": "error",
45+
"typescript/switch-exhaustiveness-check": "error",
46+
"typescript/unbound-method": "error",
47+
"typescript/use-unknown-in-catch-callback-variable": "error",
48+
"no-debugger": "error"
49+
}
50+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
"use strict";
2+
const foo = "42";
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
const foo: number = "42";
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"compilerOptions": {
3+
"target": "es2016",
4+
"lib": ["ES2024"],
5+
"module": "commonjs",
6+
"esModuleInterop": true,
7+
"forceConsistentCasingInFileNames": true,
8+
"strict": true,
9+
"skipLibCheck": true
10+
}
11+
}

apps/oxlint/src/command/lint.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ pub struct LintCommand {
5454
#[bpaf(switch, hide_usage)]
5555
pub type_aware: bool,
5656

57+
/// Enable experimental type checking (includes TypeScript compiler diagnostics)
58+
#[bpaf(switch, hide_usage)]
59+
pub experimental_type_check: bool,
60+
5761
#[bpaf(external)]
5862
pub inline_config_options: InlineConfigOptions,
5963

@@ -618,6 +622,14 @@ mod lint_options {
618622
let options = get_lint_options(".");
619623
assert!(!options.type_aware);
620624
}
625+
626+
#[test]
627+
fn experimental_type_check() {
628+
let options = get_lint_options("--experimental-type-check");
629+
assert!(options.experimental_type_check);
630+
let options = get_lint_options(".");
631+
assert!(!options.experimental_type_check);
632+
}
621633
}
622634

623635
#[cfg(test)]

apps/oxlint/src/lint.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,7 @@ impl CliRunner {
380380
// TODO: Add a warning message if `tsgolint` cannot be found, but type-aware rules are enabled
381381
let lint_runner = match LintRunner::builder(options, linter)
382382
.with_type_aware(self.options.type_aware)
383+
.with_experimental_type_check(self.options.experimental_type_check)
383384
.with_silent(misc_options.silent)
384385
.with_fix_kind(fix_options.fix_kind())
385386
.build()
@@ -1361,6 +1362,13 @@ mod test {
13611362
Tester::new().with_cwd("fixtures/tsgolint".into()).test_and_snapshot(args);
13621363
}
13631364

1365+
#[test]
1366+
#[cfg(not(target_endian = "big"))]
1367+
fn test_tsgolint_type_error() {
1368+
let args = &["--type-aware", "--experimental-type-check"];
1369+
Tester::new().with_cwd("fixtures/tsgolint_type_error".into()).test_and_snapshot(args);
1370+
}
1371+
13641372
#[test]
13651373
#[cfg(not(target_endian = "big"))]
13661374
fn test_tsgolint_no_typescript_files() {
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
---
2+
source: apps/oxlint/src/tester.rs
3+
---
4+
##########
5+
arguments: --type-aware --experimental-type-check
6+
working directory: fixtures/tsgolint_type_error
7+
----------
8+
9+
x typescript(TS2322): Type 'string' is not assignable to type 'number'.
10+
,-[index.ts:1:7]
11+
1 | const foo: number = "42";
12+
: ^^^
13+
`----
14+
15+
Found 0 warnings and 1 error.
16+
Finished in <variable>ms on 2 files using 1 threads.
17+
----------
18+
CLI result: LintFoundErrors
19+
----------
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
source: apps/oxlint/src/tester.rs
3+
---
4+
##########
5+
arguments: --type-aware
6+
working directory: fixtures/tsgolint_type_error
7+
----------
8+
Found 0 warnings and 0 errors.
9+
Finished in <variable>ms on 2 files using 1 threads.
10+
----------
11+
CLI result: LintSucceeded
12+
----------

crates/oxc_linter/src/lint_runner.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ impl Default for DirectivesStore {
126126
pub struct LintRunnerBuilder {
127127
regular_linter: Linter,
128128
type_aware_enabled: bool,
129+
experimental_type_check: bool,
129130
lint_service_options: LintServiceOptions,
130131
silent: bool,
131132
fix_kind: FixKind,
@@ -136,6 +137,7 @@ impl LintRunnerBuilder {
136137
Self {
137138
regular_linter: linter,
138139
type_aware_enabled: false,
140+
experimental_type_check: false,
139141
lint_service_options,
140142
silent: false,
141143
fix_kind: FixKind::None,
@@ -148,6 +150,12 @@ impl LintRunnerBuilder {
148150
self
149151
}
150152

153+
#[must_use]
154+
pub fn with_experimental_type_check(mut self, enabled: bool) -> Self {
155+
self.experimental_type_check = enabled;
156+
self
157+
}
158+
151159
#[must_use]
152160
pub fn with_silent(mut self, silent: bool) -> Self {
153161
self.silent = silent;
@@ -171,7 +179,11 @@ impl LintRunnerBuilder {
171179
self.regular_linter.config.clone(),
172180
self.fix_kind,
173181
) {
174-
Ok(state) => Some(state.with_silent(self.silent)),
182+
Ok(state) => Some(
183+
state
184+
.with_silent(self.silent)
185+
.with_experimental_type_check(self.experimental_type_check),
186+
),
175187
Err(e) => return Err(e),
176188
}
177189
} else {

crates/oxc_linter/src/tsgolint.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ pub struct TsGoLintState {
3232
fix: bool,
3333
/// If `true`, request that suggestions be returned from `tsgolint`.
3434
fix_suggestions: bool,
35+
/// If `true`, include TypeScript compiler syntactic and semantic diagnostics.
36+
experimental_type_check: bool,
3537
}
3638

3739
impl TsGoLintState {
@@ -46,6 +48,7 @@ impl TsGoLintState {
4648
silent: false,
4749
fix: fix_kind.contains(FixKind::Fix),
4850
fix_suggestions: fix_kind.contains(FixKind::Suggestion),
51+
experimental_type_check: false,
4952
}
5053
}
5154

@@ -67,6 +70,7 @@ impl TsGoLintState {
6770
silent: false,
6871
fix: fix_kind.contains(FixKind::Fix),
6972
fix_suggestions: fix_kind.contains(FixKind::Suggestion),
73+
experimental_type_check: false,
7074
})
7175
}
7276

@@ -80,6 +84,15 @@ impl TsGoLintState {
8084
self
8185
}
8286

87+
/// Set to `true` to include TypeScript compiler syntactic and semantic diagnostics.
88+
///
89+
/// Default is `false`.
90+
#[must_use]
91+
pub fn with_experimental_type_check(mut self, yes: bool) -> Self {
92+
self.experimental_type_check = yes;
93+
self
94+
}
95+
8396
/// # Panics
8497
/// - when `stdin` of subprocess cannot be opened
8598
/// - when `stdout` of subprocess cannot be opened
@@ -548,6 +561,8 @@ impl TsGoLintState {
548561
})
549562
.collect(),
550563
source_overrides,
564+
report_syntactic: self.experimental_type_check,
565+
report_semantic: self.experimental_type_check,
551566
}
552567
}
553568
}
@@ -573,6 +588,8 @@ pub struct Payload {
573588
pub version: i32,
574589
pub configs: Vec<Config>,
575590
pub source_overrides: Option<FxHashMap<String, String>>,
591+
pub report_syntactic: bool,
592+
pub report_semantic: bool,
576593
}
577594

578595
#[derive(Debug, Clone, Serialize, Deserialize)]

0 commit comments

Comments
 (0)