TL;DR;

TypeScript와 ESLint는 “의도적으로 무시”하는 코드에 대해 다른 접근 방식을 가집니다. TypeScript는 언더스코어(_) 접두사로 시작하는 변수를 자동으로 무시하지만, ESLint의 no-unused-vars 규칙은 기본적으로 더 엄격하므로, 언더스코어로 시작하는 변수도 사용되지 않은 것으로 간주하여 오류를 발생시킵니다. 개발자는 ESLint 설정을 통해 “의도적으로 무시”하는 코드에 대해 TypeScript와 유사한 동작을 구현하거나 더 세밀한 제어를 할 수 있습니다.

“의도적으로 무시”하는 코드

아래 예시와 같은 코드를 작성해본 경험이 있으신가요? 우리는 가끔 “의도적으로 무시” 하는 것으로 간주하는 코드를 작성해야 할 때가 있습니다.

  1. 루프 인덱스 무시: _index는 사용되지 않지만, entries() 메서드의 반환 값을 구조 분해하기 위해 필요
const items = ["apple", "banana", "orange"];
for (const [_index, value] of items.entries()) {
  console.log(value);
}
  1. 콜백 함수에서 매개변수 무시: _item은 사용되지 않지만, index에 접근하기 위해 선언
const items = ["apple", "banana", "orange"];
items.forEach((_item, index) => {
  console.log(`Index: ${index}`);
});
  1. 구조 분해 할당에서 특정 속성 무시: _age는 추출되지만 사용되지 않음을 명시
const person = { name: "John", age: 30, email: "john@example.com" };
const { name, _age, email } = person;
console.log(name, email);

TypeScript vs ESLint의 다른 견해

TypeScript 에서는 위의 예시와 같이 “의도적으로 무시”하는 코드를 작성하고 싶은 경우에 언더스코어(_) 접두사를 통해 변수를 선언하면 “이 변수는 의도적으로 사용되지 않음”을 나타냅니다. 따라서, TypeScript 컴파일러는 이에 대한 미사용 변수 경고를 생성하지 않습니다.

미사용 변수 경고를 생성하지 않는 TypeScript

그러나 우리의 코드 린팅 도구인 ESLint의 no-unused-vars기본 옵션을 살펴보면, TypeScript와는 다르게 경고를 생성하고 있습니다. ESLint는 모든 변수의 사용 여부를 철저히 검사하고, 함수 매개변수에 대해서는 마지막으로 사용된 매개변수의 미사용에 대한 경고를 발생시키는 등 타입스크립트와는 다르게 조금 더 엄격하고, 세밀한 옵션을 가지고 있습니다.

미사용에 대한 경고를 발생시키는 ESLint

typescript-eslint인데 왜 typescript를 안따라가지??

ESlint와 TypeScript의 “의도적으로 무시”하는 코드에 대한 다른 견해로 인해 개발자들은 조금 혼동이 오기도 합니다. 관련해서 검색하던 중 재미있는 discussion이 있어 이 내용을 소개합니다. 이 글을 통해 TypeScript와 ESLint의 처리방식이 어떻게 다른지, 여러분의 의견은 어떤지, 어떻게 내가 원하는데로 ESLint 규칙을 설정 하면 될지 소개하려고 합니다.

no-unused-vars ignoring _ prefixed variables 이슈 소개

no-unused-vars ignoring _ prefixed variables 이슈

이 이슈는 typescript-eslint의 no-unused-vars 규칙이 TypeScript의 기본 설정과 다르게 _로 시작하는 변수를 자동으로 무시하지 않는다는 점을 다룹니다. Johnny Reilly는 TypeScript에서는 _로 시작하는 변수가 의도적으로 사용되지 않는 것으로 간주되어 경고가 발생하지 않는데, typescript-eslint에서는 그렇지 않다는 점을 지적하고 있습니다.

이에 대해 메인테이너인 Brad Zacher는 typescript-eslint가 기본 ESLint 규칙과 일치하도록 설정되어 있으며, 모든 불필요한 변수를 자동으로 잡아내도록 설계되어 있음을 이야기하면서 아래와 같은 이유로 ESLint의 기본 설정 그대로 제공해야하는 이유를 이야기했습니다.

  • 기본 설정은 코드베이스의 표준에 대한 가정을 하지 않으며, 언더스코어를 사용한 변수 허용을 기본으로 제공하지 않음.
  • 규칙이 엄격하게 설정되어 있어 잘못된 긍정(false positives)을 피할 수 있음.
  • 사용자 정의 설정을 통해 필요한 경우 규칙을 완화할 수 있음.
  • TypeScript의 관습을 이해하지만, 단일 문자로 예외를 만드는 것이 항상 좋은 것은 아님.

이에 대해 ohnny Reilly가 typescript의 기본값을 선호하는 사람들이 있을 수 있으며, 때로는 의도적으로 무시하는 코드를 작성해야 하는 경우가 있음을 이야기 합니다. 위에서 제가 예시로 든 “(3번) 구조 분해 할당에서 특정 속성 무시”에 관해 조금 더 구체적으로 이야기를 하는데, 여기에 대해 메인테이너는 ignoreRestSiblings옵션에을 제공하고 있다고 안내하며 typescript의 기본값대로 eslint를 설정하는 방법을 문서화하는 형태로 이 이슈는 종료가 됩니다.

관련 내용

ESLint의 no-unused-vars 규칙

ESLint의 no-unused-vars 은 사용하지 않는 변수에 대해 많은 옵션을 제공합니다. 이 규칙의 기본 설정 을 파악해 보면 “의도적으로 무시”하는 코드에 대해서 TypeScript의 컴파일러 옵션보다 더 세밀하게 옵션을 설정할 수 있습니다.

{
    "rules": {
        "no-unused-vars": ["error", {
            "vars": "all",
            "args": "after-used",
            "caughtErrors": "all",
            "ignoreRestSiblings": false,
            "reportUsedIgnorePattern": false
        }]
    }
}
  • "vars": "all": 모든 변수의 사용 여부를 검사합니다. 전역 변수를 포함한 모든 변수가 대상이 됩니다.
  • "args": "after-used": 함수 매개변수에 대해서는 마지막으로 사용된 매개변수 이후의 미사용 매개변수만 경고합니다. 예를 들어, 첫 번째 매개변수가 사용되지 않더라도 두 번째 매개변수가 사용된다면 첫 번째 매개변수에 대해서는 경고하지 않습니다.
  • "caughtErrors": "all": catch 문의 에러 객체가 사용되지 않을 경우 경고합니다.
  • "ignoreRestSiblings": false: 객체 구조 분해에서 나머지 연산자(…)로 할당된 속성을 무시하지 않습니다.
  • "reportUsedIgnorePattern": false: varsIgnorePattern, argsIgnorePattern 등으로 무시되도록 설정된 변수가 실제로 사용되었을 때 이를 보고하지 않습니다.

“의도적으로 무시”하는 코드, 린트 통과하도록 설정하기

일반적으로 사용되지 않는 지역 변수와 매개변수를 표시하기 위해서는, TypeScript 보다 더 엄격한 규칙을 적용하는 ESLint @typescript-eslint/no-unused-vars를 사용하는 것을 권장합니다.

하지만 개발자의 취향에 따라 “의도적으로 무시”하는 코드에 대해서는 린트를 통과하도록 설정하고 싶을 수 있습니다. 이런 경우 세부적인 규칙 설정이 가능한 ESLint를 활용하여, TypeScript의 컴파일러 옵션보다 더 구체적인 설정이 가능합니다.

{
  "rules": {
    "@typescript-eslint/no-unused-vars": [
      "error",
      {
        "args": "all",
        "argsIgnorePattern": "^_",
        "caughtErrors": "all",
        "caughtErrorsIgnorePattern": "^_",
        "destructuredArrayIgnorePattern": "^_",
        "varsIgnorePattern": "^_",
        "ignoreRestSiblings": true
      }
    ]
  }
}
  • "error": 이 규칙을 위반하면 오류로 처리합니다.
  • "args": "all": 모든 함수 매개변수를 검사합니다.
  • "argsIgnorePattern": "^_": 언더스코어로 시작하는 함수 매개변수는 무시합니다.
  • "caughtErrors": "all": 모든 catch 문의 에러 변수를 검사합니다. (기존대로 유지)
  • "caughtErrorsIgnorePattern": "^_": 언더스코어로 시작하는 catch 문의 에러 변수는 무시합니다.
  • "destructuredArrayIgnorePattern": "^_": 배열 구조 분해에서 언더스코어로 시작하는 변수는 무시합니다.
  • "varsIgnorePattern": "^_": 언더스코어로 시작하는 일반 변수는 무시합니다.
  • "ignoreRestSiblings": true: 객체 구조 분해에서 나머지 연산자(…)를 사용한 후의 속성들은 무시합니다.

Summary

TypeScript와 ESLint는 “의도적으로 무시”하는 코드를 다르게 처리합니다. TypeScript는 언더스코어(_) 접두사로 시작하는 변수를 자동으로 무시하지만, ESLint의 no-unused-vars 규칙은 기본적으로 더 엄격합니다.

ESLint는 다양한 옵션을 통해 세밀한 제어가 가능하며, 개발자는 이를 활용하여 TypeScript와 유사한 동작을 구현하거나 자신의 선호도에 맞게 설정할 수 있습니다.

일반적으로 코드 품질 관리를 위해 ESLint의 @typescript-eslint/no-unused-vars를 사용하되, 필요에 따라 설정을 조정하여 개발 편의성과 코드 품질 사이의 균형을 유지하는 것을 추천합니다.