Pré-visualização técnica de lint orientado por tipos no Oxlint
Este post anuncia a pré-visualização técnica de lint orientado por tipos. Para o lançamento alfa atual com maior estabilidade, configuração e cobertura de regras, veja o anúncio do alfa de lint orientado por tipos.
Temos o prazer de anunciar lint orientado por tipos no oxlint!
Chegaram, enfim, no-floating-promises e regras relacionadas.
Esta prévia serve para envolver a comunidade na discussão ao documentar decisões e detalhes técnicos.
Início rápido
Se o oxlint já estiver configurado, instale oxlint-tsgolint e use a flag --type-aware:
pnpm add -D oxlint-tsgolint@latest
pnpm dlx oxlint --type-awareSe o oxlint ainda não estiver configurado e você só quiser ver no-floating-promises:
pnpm add -D oxlint-tsgolint@latest
pnpm dlx oxlint@latest --type-aware -A all -D typescript/no-floating-promisesEspera-se uma saída como:
× 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.
╭─[packages/rolldown/src/api/watch/watcher.ts:30:7]
29 │ await this.close();
30 │ originClose();
· ──────────────
31 │ };
╰────Veja o guia de uso para mais opções de configuração.
Desempenho
Em nossos testes, repositórios que antes levavam cerca de um minuto com typescript-eslint agora terminam em menos de 10 segundos.
Isso usa typescript-go, o TypeScript cerca de 10× mais rápido em Go.
Projetos do oxc-ecosystem-ci:
| Projeto | Arquivos | Tempo |
|---|---|---|
| napi-rs | 144 | 1,0 s |
| preact | 245 | 2,7 s |
| rolldown | 314 | 1,5 s |
| bluesky | 1152 | 7,0 s |
Lint orientado por tipos
Leia Rust-Based JavaScript Linters: Fast, But No Typed Linting Right Now para entender o estado atual do lint tipado na ecologia de ferramentas.
Detalhes técnicos
O núcleo da nova funcionalidade é oxc-project/tsgolint.
O tsgolint começou como protótipo em typescript-eslint/tsgolint. A equipe do typescript-eslint optou por não investir naquele protótipo e continuar o lint tipado no ESLInt via typescript-eslint.
@boshen falou com @auvred sobre um fork mais enxuto para o oxlint — apenas regras orientadas a tipos, sem resolução completa de config de linter.
@auvred continuou o desenvolvimento na organização Oxc.
Arquitetura
oxlint (Rust) e tsgolint (Go) compilam em binários separados.
oxlint faz o “frontend”: CLI, percorrimento de caminhos, ignores e impressão dos diagnósticos.
tsgolint é o backend: recebe caminhos e configuração e devolve diagnósticos estruturados.
Pipeline simples:
CLI do oxlint (passa caminhos + regras + configuração)
-> tsgolint (devolve diagnósticos)
-> CLI do oxlint (imprime diagnósticos)tsgolint
O tsgolint não fala com o typescript-go por APIs públicas.
Em vez disso compila o typescript-go com um shim que expõe APIs internas.
Todas as regras tipadas são escritas diretamente contra esses shims.
Não é o caminho recomendado para internals — mas funciona!
Como chegamos à decisão
Escrever nosso próprio type checker
Tentativas anteriores (abandonadas) incluem:
- Uma tentativa própria de inferência de tipos
- Integração do ezno (@kaleidawave)
- stc (@kdy1)
- Outras iniciativas da comunidade sem longevidade.
Há ainda Biome 2.0 em andamento com inferência própria.
Achamos inviável manter inferidor ou type checker próprio dado o ritmo de mudança do TypeScript.
Integração com o compilador TypeScript
Antes do typescript-go, projetos plugavam na API pública do TS mapeando AST para estree ou percorrendo o AST TS. Exemplos:
Também exploramos IPC com o oxlint, mas abandonamos.
Com typescript-go, o time TypeScript tende a codificar o AST TS e decodificar no lado JS por IPC.
Funciona, mas ainda há:
- problemas de desempenho incompatíveis com o perfil do oxlint;
- custo de manter mapeamento a partir do AST do TypeScript.
Pontos de atenção
O tsgolint resolve a performance, mas outros desafios permanecem.
Outra versão do TypeScript
Pretendemos publicar snapshots do typescript-go alinhados às versões do TypeScript para você instalar oxlint-typescript com a versão correta.
Contras: pode ser preciso atualizar o TypeScript quando o oxlint-tsgolint exigir mudanças.
Custo de manter o tsgolint
Shim de APIs internas tem risco. Porém AST e visitors do TS são relativamente estáveis. Aceitamos o risco e corrigimos quebras ao atualizar o typescript-go.
Nosso fork sincroniza diariamente.
Problemas de desempenho
Em monorepos enormes com centenas de projetos ou muitas project references o tsgolint ainda não escala bem.
Pode travar (deadlock) ou causar OOM em caso de bug.
Estamos atacando isso com perfil e melhorias enviadas ao typescript-go, beneficiando todos.
@camc314 já abriu vários PRs que aceleraram trechos relevantes.
Release v1.0 do tsgolint
Para v1.0 do tsgolint planejamos:
- desempenho em monorepos grandes;
- configuração fina por regra;
- correção de cada regra;
- suporte a IDE;
- estabilidade geral.
Agradecimentos
- À equipe TypeScript pelo
typescript-go. - À equipe
typescript-eslintpelo apoio. - A @auvred por criar o
tsgolint. - A @camchenry pela integração
oxlint+tsgolint. - A @camc314 pelo trabalho de performance.
Comunidade
Queremos feedback sobre oxlint e lint orientado por tipos.
- Discord: servidor
- GitHub: Discussões
- Issues: bugs do
oxlintem oxc; do lint tipado em tsgolint.
Próximos passos
Instale o oxlint:
pnpm add -D oxlint@latest oxlint-tsgolint@latest
pnpm dlx oxlint --init # gera .oxlintrc.jsonOu siga o guia de instalação.
Use a flag --type-aware:
pnpm dlx oxlint --type-awareExperimente regras tipadas em .oxlintrc.json:
{
"$schema": "./node_modules/oxlint/configuration_schema.json",
"rules": {
"typescript/await-thenable": "error",
"typescript/no-array-delete": "error",
"typescript/no-base-to-string": "error",
"typescript/no-confusing-void-expression": "error",
"typescript/no-duplicate-type-constituents": "error",
"typescript/no-floating-promises": "error",
"typescript/no-for-in-array": "error",
"typescript/no-implied-eval": "error",
"typescript/no-meaningless-void-operator": "error",
"typescript/no-misused-promises": "error",
"typescript/no-misused-spread": "error",
"typescript/no-mixed-enums": "error",
"typescript/no-redundant-type-constituents": "error",
"typescript/no-unnecessary-boolean-literal-compare": "error",
"typescript/no-unnecessary-template-expression": "error",
"typescript/no-unnecessary-type-arguments": "error",
"typescript/no-unnecessary-type-assertion": "error",
"typescript/no-unsafe-argument": "error",
"typescript/no-unsafe-assignment": "error",
"typescript/no-unsafe-call": "error",
"typescript/no-unsafe-enum-comparison": "error",
"typescript/no-unsafe-member-access": "error",
"typescript/no-unsafe-return": "error",
"typescript/no-unsafe-type-assertion": "error",
"typescript/no-unsafe-unary-minus": "error",
"typescript/non-nullable-type-assertion-style": "error",
"typescript/only-throw-error": "error",
"typescript/prefer-promise-reject-errors": "error",
"typescript/prefer-reduce-type-parameter": "error",
"typescript/prefer-return-this-type": "error",
"typescript/promise-function-async": "error",
"typescript/related-getter-setter-pairs": "error",
"typescript/require-array-sort-compare": "error",
"typescript/require-await": "error",
"typescript/restrict-plus-operands": "error",
"typescript/restrict-template-expressions": "error",
"typescript/return-await": "error",
"typescript/switch-exhaustiveness-check": "error",
"typescript/unbound-method": "error",
"typescript/use-unknown-in-catch-callback-variable": "error"
}
}


