ESLint で学ぶ TypeScript
使用する ESLint/Plugin
ルールの一覧は以下をご覧ください
ESLint ルール
ESLint Stylistic ルール
typescript-eslint ルール
初期設定
以下でまとめてインストールします
package.json
と eslint.config.mts
を作成
以下の2ファイル {
"scripts": {
"eslint-run": "eslint --flag unstable_ts_config \"**/*.{ts,tsx}\"",
"eslint-fix": "eslint --flag unstable_ts_config \"/**/*.{ts,tsx}\" --fix"
},
"devDependencies": {
"@eslint/js": "^9.13.0",
"@stylistic/eslint-plugin": "^2.9.0",
"@types/eslint__js": "^8.42.3",
"eslint": "^9.13.0",
"eslint-plugin-import": "^2.31.0",
"eslint-plugin-react": "^7.37.1",
"eslint-plugin-sort-keys-fix": "^1.1.2",
"globals": "^15.11.0",
"jiti": "^2.3.3",
"typescript": "^5.6.3",
"typescript-eslint": "^8.10.0"
},
"dependencies": {
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-router-dom": "^6.27.0"
}
}
import globals from "globals";
import importPlugin from "eslint-plugin-import";
import parserTs from "@typescript-eslint/parser";
import pluginJs from "@eslint/js";
import pluginReact from "eslint-plugin-react";
import sortKeyFix from "eslint-plugin-sort-keys-fix";
import stylistic from "@stylistic/eslint-plugin";
import tseslint from "typescript-eslint";
import * as tsEslint from "@typescript-eslint/eslint-plugin";
// ファイルを指定してfixする
// flag指定でmtsファイルも対象にできる
// npx eslint .\eslint-stylistic\6.sort-imports-false.tsx --fix --flag unstable_ts_config
export default tseslint.config({
// 設定ファイル名
name: "my-eslint-config",
// 適用するファイルパターン (全てのディレクトリの全ての.ts、.tsxファイルを対象)
files: ["**/*.{ts,tsx}"],
// 除外するファイルパターン
ignores: ["**/node_modules/**", "**/dist/**", "**/.git/**", "**/*.{js,jsx}"],
// プラグインの設定
// ...pluginReact.configs.flat.recommended,
// ...pluginReact.configs.flat['jsx-runtime'],
plugins: {
"@typescript-eslint": tsEslint, // ...tseslint.configs.strict でextendsする場合はコメントアウトする
"@stylistic": stylistic,
"import": importPlugin,
"sort-keys-fix": sortKeyFix,
},
// extends: [
// pluginJs.configs.recommended,
// ...tseslint.configs.strict,
// ...tseslint.configs.stylistic,
// ],
// その他設定
languageOptions: {
globals: globals.browser, // Webブラウザ(Client)環境の場合
// globals: globals.node, // Node.js(Server)環境の場合
ecmaVersion: "latest", // ECMAScript のバージョン
sourceType: "module", // ソースコードのタイプ(CommonJS or ESModules)
parser: parserTs, // TypeScript用Parserを指定
parserOptions: {
ecmaFeatures: {
jsx: true, // JSX(React)を使用する場合
impliedStrict: true, // TypeScript Strict Modeを有効化する場合
},
},
},
// 手動設定ルール
rules: {
"no-cond-assign": ["error", "always"],
"no-fallthrough": ["error", {
commentPattern: "fall through", // fall throughコメントアウトがあれば許可
allowEmptyCase: true,
reportUnusedFallthroughComment: true,
}],
"array-callback-return": "error",
"guard-for-in": "error",
"no-unsafe-finally": "error",
"camelcase": "error",
"dot-notation": "error",
"no-multi-str": "error",
"prefer-destructuring": "error",
"prefer-const": "error",
"prefer-arrow-callback": "error",
"no-constructor-return": "error",
"valid-typeof": "error",
"no-throw-literal": "error",
"no-async-promise-executor": "error",
"constructor-super": "error",
"@typescript-eslint/consistent-type-definitions": ["error", "interface"],
"@stylistic/quotes": ["error", "single"],
"@stylistic/comma-spacing": ["error", { "before": false, "after": true }],
"@stylistic/semi": ["error", "always"],
"@stylistic/indent": ["error", 2],
"@stylistic/line-comment-position": ["error", { "position": "above" }],
"sort-vars": ["error", { "ignoreCase": true, }],
"@stylistic/jsx-sort-props": ["error", {
"callbacksLast": true,
"shorthandFirst": false,
"shorthandLast": true,
"multiline": "ignore",
"ignoreCase": false,
"noSortAlphabetically": false,
"reservedFirst": true,
"locale": "auto"
}],
"import/first": "error",
"import/newline-after-import": "error",
"import/no-duplicates": "error",
"import/order": ["error", {
"groups": [
"builtin",
"external",
["internal", "parent", "sibling"],
"index",
"type"
],
"newlines-between": "always",
"alphabetize": {
"order": "asc",
"caseInsensitive": true
},
"warnOnUnassignedImports": true
}],
"sort-keys-fix/sort-keys-fix": "error",
"@stylistic/padding-line-between-statements": ["error",
{ "blankLine": "always", "prev": "*", "next": "*" },
{ "blankLine": "never", "prev": "*", "next": "break" },
{ "blankLine": "never", "prev": "*", "next": "import" },
{ "blankLine": "never", "prev": ["const", "let", "var"], "next": ["const", "let", "var"] },
],
"@stylistic/max-statements-per-line": ["error", { "max": 1 }],
"@stylistic/no-multiple-empty-lines": ["error", {
"max": 1,
"maxEOF": 0,
"maxBOF": 0
}],
"@stylistic/eol-last": ["error", "always"],
"@stylistic/linebreak-style": ["error", "unix"],
"@stylistic/jsx-pascal-case": ["error", {
"allowAllCaps": true,
"allowNamespace": false,
"allowLeadingUnderscore": false,
}],
"@stylistic/jsx-child-element-spacing": "error",
"@stylistic/jsx-self-closing-comp": ["error", {
"component": true,
"html": true
}],
"@stylistic/jsx-wrap-multilines": "error",
"@stylistic/jsx-closing-tag-location": ["error", "tag-aligned"],
"@stylistic/curly-newline": ["error", "always"],
"@stylistic/no-trailing-spaces": ["error", {
"skipBlankLines": false,
"ignoreComments": false
}],
}
})
npm install
npm install
Linter/Formatter 実行方法1
package.json
の scripts
に記載されている以下のコマンドを実行します
チェックだけする場合
npm run eslint-run
チェックして自動修正までする場合
npm run eslint-fix
Linter/Formatter 実行方法2
VSCodeの拡張機能
を使用し、ファイル保存時にLinter/Formatterを適用する方法です
以下の拡張機能をinstallします
以下のように VSCodeの settings.json
を設定します
{
"eslint.format.enable": true,
"editor.renderWhitespace": "all",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "always"
},
"eslint.options": {
"flags": [
"unstable_ts_config"
]
},
"typescript.updateImportsOnFileMove.enabled": "always",
"editor.defaultFormatter": null,
"files.autoSave": "onFocusChange",
"editor.formatOnType": true,
"editor.tabSize": 2,
"files.eol": "\n"
}
ESLint で学ぶ TypeScript の基本
初期設定が完了したら、test.ts
や test.tsx
ファイルを作成し、任意のコードをお試しください
1. 【命名規則】camelcase
命名規則が キャメルケース
であること
// 誤ったコード例
const test_snake_case = 'test';
// 正しいコード例
const testCamelCase = 'test';
2. 【let、const】prefer-const
再代入しない変数は、const
で定義すること
// 誤ったコード例
let letA = 'A';
letA = 'C';
let letB = 'B';
console.log(letA);
console.log(letB);
// 正しいコード例
let letA = 'A';
letA = 'C';
const letB = 'B';
console.log(letA);
console.log(letB);
3. 【template literal】no-multi-str
複数行の文字列を定義する時は、バックスラッシュではなく、バッククォート(テンプレートリテラル)
を使用すること
// 誤ったコード例
const text = '複数行';
const multiStr = 'これは' + text + 'の文章\
です。';
// 正しいコード例
const text = '複数行';
const multiStr = `これは${text}の文章
です。`;
4. 【array】array-callback-return
配列のメソッド
では、必ず return すること
// 誤ったコード例
const numbers = [1, 2, 3, 4];
const doubled = numbers.map((num) => {
num * 2;
});
console.log(doubled);
// 正しいコード例
const numbers = [1, 2, 3, 4];
const doubled = numbers.map((num) => {
return num * 2;
});
console.log(doubled);
5. 【object literal】dot-notation
ブラケット記法
ではなく、ドット記法
を使用すること
// 誤ったコード例
const obj = {
name: 'MetalMental',
};
console.log(obj['name']);
// 正しいコード例
const obj = {
name: 'MetalMental',
};
console.log(obj.name);
6. 【destructuring】prefer-destructuring
オブジェクトや配列から複数の値を取得する際は、ブラケット/ドット記法
ではなく デストラクチャリング記法
を使用すること
// 誤ったコード例
const obj = {
x: 1,
y: 2,
};
const x = obj.x;
const y = obj.y;
// 正しいコード例
const obj = {
x: 1,
y: 2,
};
const { x, y } = obj;
7. 【if】no-cond-assign
if 文で 代入演算子=
と 比較演算子===
を間違えないこと
// 誤ったコード例
let test = 'test';
if ((test = 'test')) {
console.log(test);
}
// 正しいコード例
const test = 'test';
if (test === 'test') {
console.log(test);
}
8. 【for】guard-for-in
プロトタイプチェーン
の仕組みによって継承されるプロパティのガードをすること
// 誤ったコード例
const arr = [1, 2, 3];
for (const index in arr) {
console.log(arr[index]);
}
// 正しいコード例
const arr = [1, 2, 3];
for (const index in arr) {
if (arr.hasOwnProperty(index)) {
console.log(arr[index]);
}
}
9. 【switch】no-fallthrough
switch 文の 各case
で break
すること
// 誤ったコード例
const number = Math.floor(Math.random() * 5) + 1;
switch (number) {
case 1:
console.log('数字は1です。');
break;
case 2:
console.log('数字は2です。');
case 3:
console.log('数字は2または3です。');
break;
default:
console.log('数字は1, 2, 3ではありません。');
}
// 正しいコード例
const number = Math.floor(Math.random() * 5) + 1;
switch (number) {
case 1:
console.log('数字は1です。');
break;
case 2:
console.log('数字は2です。');
break;
case 3:
console.log('数字は2または3です。');
break;
default:
console.log('数字は1, 2, 3ではありません。');
}
10. 【try-catch】no-unsafe-finally
try-catch-finally おける finally
で return しないこと
※try-catch の return は、finally の return で上書きされるため
// 誤ったコード例
function example() {
try {
return 'try block';
} catch (e) {
return 'catch block';
} finally {
return 'finally block';
}
}
console.log(example());
// 正しいコード例
function example() {
let result;
try {
result = 'try block';
} catch (e) {
result = 'catch block';
} finally {
console.log('finally block');
}
return result;
}
console.log(example());
11. 【throw】no-throw-literal
文字列を直接 throw
しないこと
※スタックトレース情報
が出力されないため
// 誤ったコード例
function riskyFunction() {
try {
throw 'エラーが起きました';
} catch (error) {
throw error;
}
}
// 正しいコード例
function riskyFunction() {
try {
throw new Error('エラーが起きました');
} catch (error) {
throw error;
}
}
12. 【type guard】valid-typeof
誤った 型ガード
をしないこと
※nunber
ではなく、number
// 誤ったコード例
function checkType(value: unknown): void {
if (typeof value === 'nunber') {
console.log('文字列です');
} else {
console.log('文字列でないです');
}
}
checkType('こんにちは'); // 文字列です
checkType(123); // 文字列でないです
// 正しいコード例
function checkType(value: unknown): void {
if (typeof value === 'number') {
console.log('文字列です');
} else {
console.log('文字列でないです');
}
}
checkType('こんにちは'); // 文字列です
checkType(123); // 文字列でないです
13. 【promise、async/await】no-async-promise-executor
promise
で async/await
を使用しないこと
※promise(then、catch) ⇔ async/await(try-catch)
// 誤ったコード例
const myPromise = new Promise(async (resolve, reject) => {
setTimeout(() => {
const success = true;
if (success) {
resolve('成功しました!');
} else {
reject('失敗しました。');
}
}, 1000);
});
myPromise
.then(result => console.log(result))
.catch(error => console.error(error));
// 正しいコード例1
const myPromise = new Promise((resolve, reject) => {
setTimeout(() => {
const success = true;
if (success) {
resolve('成功しました!');
} else {
reject('失敗しました。');
}
}, 1000);
});
myPromise
.then(result => console.log(result))
.catch(error => console.error(error));
// 正しいコード例2
const myPromise = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
const success = false;
if (success) {
resolve('成功しました!');
} else {
reject('失敗しました。');
}
}, 1000);
});
};
const fetchData = async () => {
try {
const result = await myPromise();
console.log(result);
} catch (error) {
console.error(error);
}
};
fetchData();
14. 【class、function】prefer-arrow-callback、no-constructor-return
.bind()
は使用せず、型情報が保持される アロー関数
を使用すること
constructor
は、値を初期化することが目的なため、return
しないこと
// 誤ったコード例1
class Example {
value: number;
constructor() {
this.value = 42;
}
regularFunction() {
setTimeout(function() {
console.log(this.value);
}, 1000);
}
}
const example = new Example();
example.regularFunction(); // 結果: undefined
// 誤ったコード例2
class Example {
value: number;
constructor() {
this.value = 42;
}
regularFunction() {
setTimeout(function() {
console.log(this.value);
}.bind(this), 1000);
}
}
const example = new Example();
example.regularFunction(); // 結果: 42
// 正しいコード例
class Example {
value: number;
constructor() {
this.value = 42;
}
regularFunction() {
setTimeout(() => {
console.log(this.value);
}, 1000);
}
}
const example = new Example();
example.regularFunction(); // 結果: 42
15. 【extends】constructor-super
クラスを継承
したら、必ず super()
で親クラスのコンストラクタを実行して初期化すること
// 誤ったコード例
class Animal {
constructor(public name: string) {
console.log(`Animal created: ${this.name}`);
}
}
class Dog extends Animal {
constructor(name: string) {
console.log(`Dog created: ${this.name}`);
}
}
const dog = new Dog('Buddy');
// 正しいコード例
class Animal {
constructor(public name: string) {
console.log(`Animal created: ${this.name}`);
}
}
class Dog extends Animal {
constructor(name: string) {
super(name);
console.log(`Dog created: ${this.name}`);
}
}
const dog = new Dog('Buddy');
16. 【interface、type】@typescript-eslint/consistent-type-definitions
似た機能を持つ interface
と type
どちらを使用するか統一すること?
※以下は、interfaceに統一した場合
// 誤ったコード例
type Address = {
street: string;
city: string;
};
// 正しいコード例
interface Address {
street: string;
city: string;
}
ESLint Stylistic で学ぶ コードスタイルのベストプラクティス
1. 【クォート】@stylistic/quotes
ダブルクォート
or シングルクォート
に統一にすること?
※以下は、シングルクォートに統一した場合
// 誤ったコード例
const message = "Hello, world!";
// 正しいコード例
const message = 'Hello, world!';
2. 【スペース】@stylistic/comma-spacing
カンマ,
後にスペースを入れること
// 誤ったコード例
const arr = [1,2,3];
// 正しいコード例
const arr = [1, 2, 3];
3. 【セミコロン】@stylistic/semi
各ステートメントの末尾に セミコロン;
を付けること
※TypeScriptでは、自動セミコロン挿入 (ASI)
機能があるため、各ステートメントの末尾にセミコロンを付けなくても動作はするが、予期しない動作を防ぐためにセミコロンは付けるようにする
// 誤ったコード例
const name = 'ESLint'
// 正しいコード例
const name = 'ESLint';
4. 【インデント】@stylistic/indent
インデントのスペースを統一すること
※以下は、2スペースに統一した場合
// 誤ったコード例
const person = {
name: 'John',
age: 30,
address: {
street: '123 Main St',
city: 'New York',
zip: '10001'
},
hobbies: ['reading', 'travelling']
};
// 正しいコード例
const person = {
name: 'John',
age: 30,
address: {
street: '123 Main St',
city: 'New York',
zip: '10001'
},
hobbies: ['reading', 'travelling']
};
5. 【コメント】@stylistic/line-comment-position
コメントアウトは、横
ではなく 上
に記載すること
// 誤ったコード例
const myName = 'MetalMental'; // 私の名前を定義する
// 正しいコード例
// 私の名前を定義する
const myName = 'MetalMental';
6. 【ソート】eslint-plugin-import、eslint-plugin-sort-keys-fix、@stylistic/jsx-sort-props
import
objectのkey
jsxのprops
をアルファベット順にソートすること
※sort-imports
と sort-keys
というルールがESLint本体にありますが、自動fix機能がない
ため、プラグインを使用しています
// 誤ったコード例
import { useState } from 'react';
import React from 'react';
import { Link } from 'react-router-dom';
const App = () => {
const unsortedConfig = {
name: 'John',
course: 'Mathmatics',
isStudent: true,
age: 25,
};
const [count, setCount] = useState(0);
return (
<div>
<button
data-isStudent={unsortedConfig.isStudent}
data-course={unsortedConfig.course}
onClick={() => setCount(count + 1)}
data-age={unsortedConfig.age}
data-name={unsortedConfig.name}
>
Click me: {count}
</button>
<Link to="/home">Go Home</Link>
</div>
);
};
export default App;
// 正しいコード例
import React, { useState } from 'react';
import { Link } from 'react-router-dom';
const App = () => {
const unsortedConfig = {
age: 25,
course: 'Mathmatics',
isStudent: true,
name: 'John',
};
const [count, setCount] = useState(0);
return (
<div>
<button
data-age={unsortedConfig.age}
data-course={unsortedConfig.course}
data-isStudent={unsortedConfig.isStudent}
data-name={unsortedConfig.name}
onClick={() => setCount(count + 1)}
>
Click me: {count}
</button>
<Link to="/home">Go Home</Link>
</div>
);
};
export default App;
7. 【ファイル】@stylistic/eol-last、@stylistic/linebreak-style
ファイルの改行コードを LF
にすること
ファイルの末尾が 改行
で終わること
8. 【改行】@stylistic/curly-newline
ブロックステートメント{}
は、改行すること
// 誤ったコード例
if (true) { console.log('true'); }
// 正しいコード例
if (true) {
console.log('true');
}
9. 【空行】@stylistic/padding-line-between-statements、@stylistic/max-statements-per-line、@stylistic/no-multiple-empty-lines
以下を制限、統一すること
- 各ステートメント前後の空行
- 連続してよい空行の数
- 1行におけるステートメントの数
// 誤ったコード例
import React, { useState } from 'react';
import { Link } from 'react-router-dom';
const x = 5;const y = 6;const z = 7;
function foo() {
const b = 2;
const c = 3;
}
function bar() {
return 8;
}
function hoge() {
return 9;
}
// 正しいコード例
import React, { useState } from 'react';
import { Link } from 'react-router-dom';
const x = 5;
const y = 6;
const z = 7;
function foo() {
const b = 2;
const c = 3;
}
function bar() {
return 8;
}
function hoge() {
return 9;
}
10. 【JSX】@stylistic/jsx-pascal-case、@stylistic/jsx-child-element-spacing、@stylistic/jsx-self-closing-comp、@stylistic/jsx-wrap-multilines、@stylistic/jsx-closing-tag-location
以下を制限、統一すること
- コンポーネント名は、大文字から始まる
パスカルケース
にする - imgタグ等で、余分な
終了タグ
は省略する - 複数行のJSXの場合は、
()
をつける - インライン要素(bタグ等)とテキストが隣接する場合は、
{' '}
を使用する - 開始タグと終了タグを整列させる
※パスカルケースの理由は、小文字のHTMLタグ
と 大文字のコンポーネント
で区別するため
// 誤ったコード例
import React from 'react';
const _exampleComponent: React.FC = () => {
return <div>
<img src="image.jpg"></img>
<b>This text</b>
is bold
</div>;
};
const HelloWorld: React.FC = () => {
return (
<div>
<_exampleComponent />
</div>
);
};
// 正しいコード例
import React from 'react';
const ExampleComponent: React.FC = () => {
return (
<div>
<img src="image.jpg" />
<b>This text</b>{' '}
is bold
</div>
);
};
const HelloWorld: React.FC = () => {
return (
<div>
<ExampleComponent />
</div>
);
};
Discussion