Skip to content

Commit 6a6637e

Browse files
committed
type aware linting blog post
1 parent 68e80c6 commit 6a6637e

File tree

6 files changed

+289
-4
lines changed

6 files changed

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

0 commit comments

Comments
 (0)