Skip to content

Oxlint 타입 인식 사전 보기

본 게시물은 타입 인식 린팅의 기술적 사전 보기 발표입니다. 개선된 안정성, 구성 가능성 및 규칙 커버리지로 향상된 최신 알파 릴리스는 타입 인식 린팅 알파 발표를 참조하세요.


oxlint에서 타입 인식 린팅을 발표하게 되어 매우 기쁩니다!

오랫동안 기다려온 no-floating-promises 및 관련 규칙이 도입되었습니다.

이 사전 릴리스는 결정 과정과 기술 세부 사항을 문서화함으로써 공동체와 협업하고 논의하는 것을 목표로 합니다.

빠른 시작

oxlint가 이미 구성되어 있다면, oxlint-tsgolint를 설치하고 --type-aware 플래그를 사용하여 oxlint를 실행하세요:

bash
pnpm add -D oxlint-tsgolint@latest
pnpm dlx oxlint --type-aware

oxlint가 아직 구성되지 않았지만 no-floating-promises를 실제로 확인하고 싶다면:

bash
pnpm add -D oxlint-tsgolint@latest
pnpm dlx oxlint@latest --type-aware -A all -D typescript/no-floating-promises

예를 들어 다음과 같은 출력을 기대할 수 있습니다:

js
 × typescript-eslint(no-floating-promises): 프로미스는 대기해야 하며, .catch 호출로 끝나야 하거나, 거절 처리기가 있는 .then 호출로 끝나야 하거나, `void` 연산자로 명시적으로 무시되었어야 합니다.
   ╭─[packages/rolldown/src/api/watch/watcher.ts:30:7]
29await this.close();
30originClose();
   ·       ──────────────
31 │     };
   ╰────

추가 설정 옵션에 대해서는 사용 가이드를 방문하세요.

성능

테스트 결과, 이전에는 typescript-eslint로 실행 시 1분이 걸렸던 저장소들이 이제 10초 미만에 완료됩니다.

이는 10배 빠른 타입스크립트로 작성된 typescript-go를 활용함으로써 달성했습니다.

oxc-ecosystem-ci의 프로젝트를 사용해 테스트한 결과:

프로젝트파일 수시간
napi-rs1441.0s
preact2452.7s
rolldown3141.5s
bluesky11527.0s

타입 인식 린팅

현재 생태계에서 타입 인식 린팅의 상태를 이해하려면 다음 글을 참고하세요: Rust 기반 자바스크립트 린터: 빠르지만 지금은 타입 인식 린팅 없음

기술 세부 사항

이 새로운 기능의 핵심은 oxc-project/tsgolint입니다.

tsgolint 프로젝트는 처음에는 typescript-eslint/tsgolint로 초기 프로토타입화되었습니다. 하지만 typescript-eslint 팀은 이 프로토타입에 개발 리소스를 할당하지 않기로 결정했으며, 그들은 타입 인식 린팅을 위한 ESLint 기반 작업을 계속 진행할 계획입니다.

@boshenoxlint에 맞춰 수정된, 단순화된 버전을 위해 @auvred에게 제안했습니다. 이 버전은 전체 린터가 필요로 하는 정교한 구성 해결 기능 없이 타입 인식 규칙만 포함합니다.

@auvred는 오픈소스 조직인 Oxc 내에서 이 프로젝트를 계속 개발해 주겠다고 막연히 제공했습니다.

아키텍처

oxlint(Rust로 작성됨)과 tsgolint(Go로 작성됨)은 각각 자체 바이너리로 컴파일됩니다.

oxlinttsgolint의 "프론트엔드" 역할을 하며, CLI, 경로 순회, 무시 로직, 진단 출력을 담당합니다.

tsgolintoxlint의 백엔드 역할을 하며, 경로와 구성 정보를 입력으로 받아 구조화된 진단 정보를 출력합니다.

이로 인해 간단한 파이프라인이 생성됩니다:

oxlint CLI (경로 + 규칙 + 구성 정보 반환)
  -> tsgolint (진단 정보 반환)
  -> oxlint CLI (진단 정보 출력)

tsgolint

tsgolint는 공개 API를 통해 typescript-go와 통신하지 않습니다.

대신, 내부 API를 셰임(임시 래핑)하여 공개하게 만듭니다.

모든 타입 인식 규칙은 이 셰임된 API에 직접 작성됩니다.

비록 이 방법이 내부 접근을 위한 추천 방식은 아니지만, 작동합니다!

결정 과정

자체 타입 체커 개발

이전에 실패한 타입 체커 구현 시도들에는 다음이 포함됩니다:

또한 현재 진행 중인 Biome 2.0는 자체 타입 추론 구현을 하고 있습니다.

우리는 타입 추론기나 타입 체커를 자체 개발하는 것은 불가능하다고 판단했습니다. 왜냐하면 타입스크립트처럼 빠르게 변화하는 대상을 따라가는 것은 어려웠기 때문입니다.

타입스크립트 컴파일러와의 통신

typescript-go 이전에는, 프로젝트들이 타입스크립트의 공개 API에 플러그인 인터페이스를 추가하기 위해 타입스크립트의 AST를 estree로 매핑하거나, 타입스크립트의 직접적인 트래버설을 수행했습니다. 예시로는:

또한 oxlint와의 프로세스 간 통신을 탐색했지만, 아이디어를 포기했습니다.

typescript-go를 통해 타입스크립트 팀은 인터프로세스 통신을 통해 타입스크립트의 AST를 인코딩하고, 자바스크립트 쪽에서 디코딩하는 방향으로 나아가고 있습니다.

이러한 접근법은 작동하지만, 여전히 다음과 같은 문제가 있습니다:

  • oxlint의 성능 특성에 부적합한 정도의 다양한 성능 문제
  • 타입스크립트의 AST에서 estree로의 매핑을 유지 관리하는 비용

고려사항

tsgolint는 성능 문제를 해결하지만, 다른 기술적 과제들이 여전히 해결되어야 합니다.

다른 타입스크립트 버전 요구

우리는 typescript-go 버전의 스냅샷을 출시하고, 이를 타입스크립트 버전과 동기화할 계획입니다. 그러면 oxlint-typescript를 올바른 타입스크립트 버전과 함께 설치할 수 있습니다.

이 접근법의 단점은 oxlint-tsgolint가 변경을 요구할 경우 타입스크립트를 업그레이드해야 할 수도 있다는 점입니다.

tsgolint 유지보수 비용

타입스크립트의 내부 API를 셰임하는 것은 일정한 리스크를 수반합니다. 하지만 실제로 타입스크립트의 AST와 그 방문자들은 매우 안정적입니다. 우리는 이러한 리스크를 수용하며, typescript-go 업그레이드 시 깨지는 부분을 수정할 것입니다.

우리의 typescript-go 버전은 매일 동기화됩니다.

성능 문제

현재 tsgolint는 수백 개의 프로젝트 또는 많은 프로젝트 참조를 가진 대규모 모노레포에서는 잘 작동하지 않습니다.

버그가 발생할 경우 데드락에 빠지거나 메모리 초과(주변 메모리 부족, OOM)를 유발할 수 있습니다.

우리는 이러한 문제들을 적극적으로 해결하고 있으며, typescript-go에 프로파일링 및 개선을 제출하여 모든 typescript-go 사용자에게 이익을 줍니다.

우리의 핵심 팀 멤버 @camc314는 이미 몇몇 중요한 프로젝트 요청(기여)을 제출하여 여러 코드 경로를 크게 빠르게 만들었습니다.

v1.0 릴리스

tsgolint v1.0에서는 다음을 해결할 예정입니다:

  • 대규모 모노레포의 성능 문제
  • 개별 규칙 구성 가능
  • 각 개별 규칙의 정확성
  • IDE 지원
  • 전반적인 안정성

감사의 말

다음 분들에게 감사를 전합니다:

  • typescript-go를 만들어준 타입스크립트 팀
  • 따뜻한 지지를 보내준 typescript-eslint
  • tsgolint를 만들었던 @auvred
  • oxlint + tsgolint 통합을 해준 @camchenry
  • 성능 문제 해결에 기여한 @camc314

커뮤니티 참여

oxlint와 타입 인식 린팅에 대한 피드백을 들려주시기를 기대하며, 여러분의 개발 워크플로우 향상에 어떻게 도움이 될지 보는 것을 기대합니다.

우리와 연결하세요:

  • 디스코드: 실시간 토론을 위해 커뮤니티 서버에 참여하세요
  • 깃허브: 깃허브 토론에서 피드백을 공유하세요
  • 이슈: oxlint 버그는 oxc에, 타입 인식 린팅 버그는 tsgolint에 보고하세요.

다음 단계

oxlint를 설치하세요:

bash
pnpm add -D oxlint@latest oxlint-tsgolint@latest
pnpm dlx oxlint --init # .oxlintrc.json 생성

또는 설치 가이드를 따르세요.

--type-aware CLI 플래그를 사용하세요.

bash
pnpm dlx oxlint --type-aware

.oxlintrc.json에서 어떤 타입 인식 규칙도 자유롭게 실험해 보세요:

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"
  }
}