Skip to content

Commit 96b53ce

Browse files
committed
type aware linting blog post
1 parent 68e80c6 commit 96b53ce

File tree

6 files changed

+287
-4
lines changed

6 files changed

+287
-4
lines changed

.vitepress/config/en.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export const enConfig = defineLocaleConfig("root", {
2323
{
2424
text: "Resources",
2525
items: [
26-
{ text: "Blog", link: "/blog/2025-06-10-oxlint-stable" },
26+
{ text: "Blog", link: "/blog/2025-08-18-oxlint-type-aware" },
2727
{ text: "Team", link: "/team" },
2828
{ text: "Branding", link: "/branding" },
2929
{ text: "Website GitHub", link: "https://github.com/oxc-project/oxc-project.github.io" },

.vitepress/config/inlined-scripts/banner.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,6 @@
66
}
77
};
88

9-
window.__OXC_BANNER_ID__ = "/blog/2025-06-10-oxlint-stable";
9+
window.__OXC_BANNER_ID__ = "/blog/2025-08-18-oxlint-type-aware";
1010
restore(`oxc-banner-dismissed-${__OXC_BANNER_ID__}`, "banner-dismissed");
1111
})();

.vitepress/sidebar.blog.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
[
2+
{
3+
"text": "Oxlint Type-Aware Preview",
4+
"link": "/blog/2025-08-18-oxlint-type-aware"
5+
},
26
{
37
"text": "Oxlint v1.0 Stable",
48
"link": "/blog/2025-06-10-oxlint-stable"

.vitepress/theme/components/Banner.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ const hide = () => {
1717
};
1818
1919
// Sync with inlined-scripts/banner.js
20-
const slug = "/blog/2025-06-10-oxlint-stable";
20+
const slug = "/blog/2025-08-18-oxlint-type-aware";
2121
2222
const bannerDismissed = useLocalStorage<boolean>(`oxc-banner-dismissed-${slug}`, false);
2323
@@ -46,7 +46,7 @@ const dismiss = () => {
4646

4747
<template>
4848
<div ref="el" class="banner banner-dismissed">
49-
<div class="text"><a :href="slug">Announcing Oxlint v1.0 Stable</a> 🎉</div>
49+
<div class="text"><a :href="slug">Announcing Oxlint Type-Aware Preview</a> 🎉</div>
5050

5151
<button type="button" @click="dismiss">
5252
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">

.vitepress/theme/constants/team.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,18 @@ export const CORE_MEMBERS: TeamMember[] = [
152152
{ icon: "x", link: "https://x.com/yagiznizipli" },
153153
],
154154
},
155+
{
156+
id: "auvred",
157+
type: "consultant",
158+
name: "auvred",
159+
title: "tsgolint",
160+
avatar: "https://www.github.com/auvred.png",
161+
org: "typescript-eslint",
162+
orgLink: "https://github.com/typescript-eslint",
163+
links: [
164+
{ icon: "github", link: "https://github.com/auvred" },
165+
],
166+
},
155167
];
156168

157169
export const CORE_TEAM_MEMBERS = CORE_MEMBERS.filter(({ type }) => type === "core") as CoreTeamMember[];
Lines changed: 267 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,267 @@
1+
---
2+
title: Oxlint Type-Aware Preview
3+
outline: deep
4+
authors:
5+
- boshen
6+
- auvred
7+
- camchenry
8+
- cameron
9+
---
10+
11+
<AppBlogPostHeader />
12+
13+
<br>
14+
15+
We're thrilled to announce type-aware rules support in `oxlint`!
16+
17+
This preview release aims to engage with the community for collaboration and
18+
discussion by documenting our decision process and technical details.
19+
20+
## Quick Start
21+
22+
If `oxlint` isn't already installed, please visit our [usage guide](https://oxc.rs/docs/guide/usage/linter.html).
23+
24+
To get started, run:
25+
26+
```bash
27+
pnpm add -D oxlint-tsgolint@latest
28+
pnpm dlx oxlint --type-aware
29+
```
30+
31+
Or only `no-floating-promises` in action:
32+
33+
```bash
34+
pnpm add -D oxlint-tsgolint@latest
35+
pnpm dlx oxlint --type-aware -A all -D typescript/no-floating-promises
36+
```
37+
38+
We expect to see, for example:
39+
40+
```js
41+
× typescript-eslint(no-floating-promises): Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler or be explicitly marked as ignored with the `void` operator.
42+
╭─[packages/rolldown/src/api/watch/watcher.ts:30:7]
43+
29await this.close();
44+
30originClose();
45+
· ──────────────
46+
31 │ };
47+
╰────
48+
```
49+
50+
## Performance
51+
52+
Our testing shows that large repositories, which previously took several minutes to run with `typescript-eslint`,
53+
now complete in less than 10 seconds.
54+
55+
This is achieved by leveraging [`typescript-go`](https://github.com/microsoft/typescript-go),
56+
the [10x faster TypeScript](https://devblogs.microsoft.com/typescript/typescript-native-port) written in Go.
57+
58+
Using projects from [oxlint-ecosystem-ci](https://github.com/oxc-project/oxlint-ecosystem-ci):
59+
60+
| Project | Files | Time |
61+
| -------- | ----- | ---- |
62+
| napi-rs | 144 | 1.0s |
63+
| preact | 245 | 2.7s |
64+
| rolldown | 314 | 1.5s |
65+
| bluesky | 1152 | 7.0s |
66+
67+
## Type-Aware Linting
68+
69+
Please refer to
70+
[Rust-Based JavaScript Linters: Fast, But No Typed Linting Right Now](https://www.joshuakgoldberg.com/blog/rust-based-javascript-linters-fast-but-no-typed-linting-right-now)
71+
to understand the current status of type-aware linting in the ecosystem.
72+
73+
## Technical Details
74+
75+
The core of this new functionality is [oxc-project/tsgolint](https://github.com/oxc-project/tsgolint).
76+
77+
The tsgolint project was initially prototyped as [typescript-eslint/tsgolint](https://github.com/typescript-eslint/tsgolint).
78+
However, the `typescript-eslint` team decided not to allocate development resources to this prototype, as they plan to continue their work on `typescript-eslint` for typed linting with ESLint.
79+
80+
[@boshen](https://github.com/Boshen) reached out to [@auvred](https://github.com/auvred) for a forked, scoped-down version adapted for oxlint.
81+
This version would only contain type-aware rules without the sophisticated configuration resolution a full linter would require.
82+
83+
[@auvred](https://github.com/auvred) generously offered to continue its development under the Oxc organization.
84+
85+
### Architecture
86+
87+
`oxlint` (written in Rust) and `tsgolint` (written in Go) are compiled into their own binaries.
88+
89+
`oxlint` serves as the "frontend" for `tsgolint`, handling the CLI, path traversal, ignore logic, and diagnostic printing.
90+
91+
`tsgolint` acts as the backend for `oxlint`, accepting paths and configuration as input and outputting structured diagnostics.
92+
93+
This creates a simple pipeline:
94+
95+
```
96+
oxlint CLI (returns paths + rules + configuration)
97+
-> tsgolint (returns diagnostics)
98+
-> oxlint CLI
99+
```
100+
101+
### `tsgolint`
102+
103+
`tsgolint` does not communicate with typescript-go via public APIs.
104+
105+
Instead, it compiles `typescript-go` by [shimming its internal APIs to make them public](https://github.com/oxc-project/tsgolint/tree/main/shim).
106+
107+
All type-aware rules are written directly against these shimmed APIs.
108+
109+
While this isn't the recommended approach for accessing internals, it works!
110+
111+
## Decision Process
112+
113+
### Write our own type checker
114+
115+
Previous abandoned attempts to implement a type-checker included:
116+
117+
- My own attempt at [writing type inference](https://gist.github.com/Boshen/d189de0fe0720a30c5182cb666e3e9a5)
118+
- [Integrate](https://github.com/oxc-project/oxc/pull/413) [ezno type checker](https://github.com/kaleidawave/ezno) by [@kaleidawave](https://github.com/kaleidawave)
119+
- [stc](https://github.com/dudykr/stc) by [@kdy1](https://github.com/kdy1)
120+
- (There are also many attempts in the community that did not go far).
121+
122+
Additionally, there's the work-in-progress [Biome 2.0](https://biomejs.dev/blog/biome-v2/) with its own type-inference implementation.
123+
124+
We determined that writing our own type-inferencer or type-checker was not feasible due to
125+
the challenge of keeping up with a fast-moving target like TypeScript.
126+
127+
### Communication with TypeScript Compiler
128+
129+
Prior to `typescript-go`, projects added plugin interfaces to TypeScript's public API by either mapping its AST to `estree` or directly traversing the TypeScript AST. Examples include:
130+
131+
- [typescript-eslint](https://typescript-eslint.io/getting-started/typed-linting)
132+
- [tsslint](https://github.com/johnsoncodehk/tsslint)
133+
- [tsl](https://github.com/ArnaudBarre/tsl)
134+
135+
We also explored [inter-process communication with oxlint](https://github.com/oxc-project/oxc/discussions/2855) but abandoned the idea.
136+
137+
With `typescript-go`, the TypeScript team is [leaning towards](https://github.com/microsoft/typescript-go/discussions/455)
138+
encoding the TypeScript AST and decoding it on the JavaScript side through inter-process communication.
139+
140+
While these approaches work, they still incur:
141+
142+
- Performance issues of varying degrees that don't suit oxlint's performance characteristics.
143+
- The cost of maintaining an AST mapping from TypeScript's AST.
144+
145+
## Considerations
146+
147+
While `tsgolint` solves the performance issue, there are other technical challenges that need to be addressed.
148+
149+
### Requirement for a Different TypeScript Version
150+
151+
We plan to release snapshots of `typescript-go` versions and align their version numbers with TypeScript.
152+
You will then be able to install `oxlint-typescript` with the correct TypeScript version.
153+
154+
The downside of this approach is that you may need to upgrade TypeScript if `oxlint-tsgolint` requires changes.
155+
156+
### Maintenance cost of `tsgolint`
157+
158+
Shimming TypeScript's internal APIs carries some risk. However, the TypeScript AST and its visitor are actually quite stable.
159+
We accept this risk and will fix breaking changes when upgrading `typescript-go`.
160+
161+
Our `typescript-go` version is synced many times a week.
162+
163+
## Performance Issues
164+
165+
`tsgolint` currently does not perform well on large monorepos with hundreds of projects or lots of project references.
166+
167+
It may hang or cause out-of-memory if a bug is encountered.
168+
169+
We are actively profiling and submitting improves to `typescript-go`, benefiting all `typescript-go` users.
170+
171+
Our core team member [@camc314](https://github.com/camc314) has already submitted [many PRs](https://github.com/microsoft/typescript-go/pulls?q=is%3Apr+author%3Acamc314+) that made several code paths significantly faster.
172+
173+
## v1.0 Release
174+
175+
For `tsgolint` v1.0, we will address:
176+
177+
* performance issue for large monorepos
178+
* be able to configure individual rules
179+
* correctness of each individual rules
180+
* overall stability
181+
182+
## Acknowledgements
183+
184+
We'd like to extend our gratitude to:
185+
186+
- The TypeScript team for creating `typescript-go`.
187+
- The `typescript-eslint` team for their heartwarming support.
188+
- [@auvred](https://github.com/auvred) for creating `tsgolint`.
189+
- [@camchenry](https://github.com/camchenry) for the `oxlint` + `tsgolint` integration.
190+
- [@camc314](https://github.com/camc314) for work on performance issues.
191+
192+
## Join the Community
193+
194+
We'd love to hear your feedback on oxlint and type-aware linting and are excited to see how it helps improve your development workflow.
195+
196+
Connect with us:
197+
198+
- **Discord**: Join our [community server](https://discord.gg/9uXCAwqQZW) for real-time discussions
199+
- **GitHub**: Share feedback on [GitHub Discussions](https://github.com/oxc-project/oxc/discussions)
200+
- **Issues**: Report bugs or request features on our [issue tracker](https://github.com/oxc-project/oxc/issues)
201+
202+
## Give It a Try
203+
204+
To get started, install `oxlint`:
205+
206+
```bash
207+
pnpm add -D oxlint@latest oxlint-tsgolint@latest
208+
pnpm dlx oxlint --init # generate .oxlintrc.json
209+
```
210+
211+
or follow the [installation guide](https://oxc.rs/docs/guide/usage/linter).
212+
213+
Use the `--type-aware` CLI flag.
214+
215+
```
216+
pnpm dlx oxlint --type-aware
217+
```
218+
219+
Or play around with any of the type-aware rules in `.oxlintrc.json`:
220+
221+
```json
222+
{
223+
"$schema": "./node_modules/oxlint/configuration_schema.json",
224+
"rules": {
225+
"typescript/await-thenable": "error",
226+
"typescript/no-array-delete": "error",
227+
"typescript/no-base-to-string": "error",
228+
"typescript/no-confusing-void-expression": "error",
229+
"typescript/no-duplicate-type-constituents": "error",
230+
"typescript/no-floating-promises": "error",
231+
"typescript/no-for-in-array": "error",
232+
"typescript/no-implied-eval": "error",
233+
"typescript/no-meaningless-void-operator": "error",
234+
"typescript/no-misused-promises": "error",
235+
"typescript/no-misused-spread": "error",
236+
"typescript/no-mixed-enums": "error",
237+
"typescript/no-redundant-type-constituents": "error",
238+
"typescript/no-unnecessary-boolean-literal-compare": "error",
239+
"typescript/no-unnecessary-template-expression": "error",
240+
"typescript/no-unnecessary-type-arguments": "error",
241+
"typescript/no-unnecessary-type-assertion": "error",
242+
"typescript/no-unsafe-argument": "error",
243+
"typescript/no-unsafe-assignment": "error",
244+
"typescript/no-unsafe-call": "error",
245+
"typescript/no-unsafe-enum-comparison": "error",
246+
"typescript/no-unsafe-member-access": "error",
247+
"typescript/no-unsafe-return": "error",
248+
"typescript/no-unsafe-type-assertion": "error",
249+
"typescript/no-unsafe-unary-minus": "error",
250+
"typescript/non-nullable-type-assertion-style": "error",
251+
"typescript/only-throw-error": "error",
252+
"typescript/prefer-promise-reject-errors": "error",
253+
"typescript/prefer-reduce-type-parameter": "error",
254+
"typescript/prefer-return-this-type": "error",
255+
"typescript/promise-function-async": "error",
256+
"typescript/related-getter-setter-pairs": "error",
257+
"typescript/require-array-sort-compare": "error",
258+
"typescript/require-await": "error",
259+
"typescript/restrict-plus-operands": "error",
260+
"typescript/restrict-template-expressions": "error",
261+
"typescript/return-await": "error",
262+
"typescript/switch-exhaustiveness-check": "error",
263+
"typescript/unbound-method": "error",
264+
"typescript/use-unknown-in-catch-callback-variable": "error"
265+
}
266+
}
267+
```

0 commit comments

Comments
 (0)