biomejs/biome

☂️ Type-aware linter

Conaclos opened this issue · 19 comments

Description

This umbrella issue tracks the development of type-aware lint rules.
We first motivate our decision to implement our own type synthesizer, and then present the type-aware rules we intend to implement. In a second section, we present preliminary design material that indicates the high-level direction we want to take.

Motivation

Multiple rules from TypeScript ESLint requires type information. Moreover, several linter rules could be enhanced by using type information.

TypeScript ESLint uses the TypeScript Compiler API to get types. This architecture has the advantage of using the TypeScript Compiler. However, it has several drawbacks:

  • TSC is slow, and using the Compiler API makes the slowness even more noticeable.
    In fact, it is so slow that TypeScript ESLint provides presets with and without the rules that require type information.
  • Biome and TSC use their own AST, which makes interoperability difficult.

This is why we think it is important to implement our own type synthesiser. If we have a fast type synthesiser, then we could enhance many lint rules with a marginal performance overhead. Note that we are not trying to implement a full-fledged type system or a type checker like TypeScript. Many attempt, even the most promising failed.

We believe that the Biome type synthesiser doesn't need to be perfect or handle complex TypeScript types. Even a rudimentary type synthesiser could be valuable to many lint rules.

Type-aware lint rules

A first design and implementation of the Biome type synthesiser aims to implement a preliminary version of the following rules:

  • useAwaitThenable (await-thenable)

    Ensure that only thenable values are awaited. We could first target a rule that ensures that an awaited expression is a Promise. We could ignore values with an unknown type.

  • noFloatingPromises (no-floating-promises)

    Ensure that a promise is handled (returned, awaited, ...).

  • noForInArray (no-for-in-array)

    Ensure that for-in is not used on arrays.

  • noDuplicateLiteralEnumMembers (no-duplicate-enum-values)

    Ensure that every enum member initialized with a literal expression is unique. This doesn't necessarlly requires a type system. We need to compute literal expressions.

Funding

To support this effort, please consider sponsoring Biome within our Open Collective or GitHub sponsorship.

We decide not to fund this issue with Polar.sh because it is a long term effort. Polar.sh is designed for short and medium term efforts. It set a deadline of 6 months to complete a task.

Maybe the approach is to identify the most critical rules, and implement type inference just for the required types? E.g. the first two rules appear to be promise-related, so figure out that a given identifier is a promise. This should still be built on a forward-looking type analyzer architecture, but by constraining the scope, you can make the effort much more manageable, since you don't have to achieve parity with tsc (a fool's errand anyway, which you point out). This also means that, post an admittedly non-trivial chunk of groundwork, additional rules can be developed (and shipped, ideally!) incrementally.

If the duplicate enum rule doesn't require a type checker, it should probably be peeled off into its own issue. Seems like it's bundled here just because it comes from typescript-eslint.

I use tsc --noEmit && biome check . in my package.json. Can I drop tsc? What will I miss if I drop tsc?

I use tsc --noEmit && biome check . in my package.json. Can I drop tsc? What will I miss if I drop tsc?

Biome doesn't perform type checking and is not intended to do in the future.
Once this issue is resolved, Biome will be able to implement some rules from Typescript ESLint plugin that requires type information.

Is it possible to give an extremely rough time frame for when this could become available? (Like is it on the order of weeks, months, or years).

I ask because if it's weeks or even a few months I could probably wait for noFloatingPromises but if it's longer than 2-3 months I'll probably stick to eslint since that rule catches many many bugs. (Ran into two today which is why I'm here.)

I ask because if it's weeks or even a few months I could probably wait for noFloatingPromises but if it's longer than 2-3 months I'll probably stick to eslint since that rules catches many many bugs. (Ran into two today which is why I'm here.)

@scamden this is offtopic but in this repo we run both biome (because it's 100x faster than eslint) and eslint (because of type-aware rules like no-floating-promises). This approach works fine for us. Eslint is faster than it used to be as well, because it only lints with a couple of rules, and we run it in parallel with tests anyway because it doesn't do any autofixing.

@scamden this is offtopic but in this repo we run both biome (because it's 100x faster than eslint) and eslint (because of type-aware rules like no-floating-promises). This approach works fine for us. Eslint is faster than it used to be as well, because it only lints with a couple of rules, and we run it in parallel with tests anyway because it doesn't do any autofixing.

Thanks for the idea @krzkaczor. Out of curiosity how does biome add value in that setup? Just trying to slightly reduce the runtime of the eslint by running fewer rules?

@scamden yeah, and the ability to run eslint with tests in parallel (b/c eslint won't change any code). Overall it was more than 50% speedup in our setup.

@scamden The link to the repo isn't available anymore. Could you please put it back? TIA

what about just shipping a default tsconfig.json, and then tell|ask|suggest Biome users in the docs to use it?