この記事について
NodeJS環境で実行するサーバサイドアプリケーション開発において
新規プロジェクト(リポジトリ)に TypeScript、フォーマッタ(Prettier)、リンタ(ESLint)、単体テストツール(Jest)を導入する手順のメモ
※適宜加筆修正を行っていく予定
手順
package.json の生成
npm init -y
TypeScript の導入
TypeScript本体とNodeJS組み込み機能の型定義パッケージをインストール
npm install --save-dev typescript @types/node
tsconfig.json を設定
以下のコマンドで、コメント付きの tsconfig.json ファイルが生成されるのでいくつか設定を変更する
npx tsc --init
以下の設定例ではソースコードを src ディレクトリ、生成物を dist ディレクトリに配置する設定となっている
{
"compilerOptions": {
"incremental": true,
"tsBuildInfoFile": "./.tsbuildinfo",
"target": "ES2021",
"lib": ["ES2021"],
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"module": "commonjs",
"moduleResolution": "node",
"rootDir": "./src",
"baseUrl": "./src",
"paths": {
"~/*": ["./*"]
},
"resolveJsonModule": true,
"allowJs": true,
"checkJs": true,
"outDir": "./dist",
"isolatedModules": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"exactOptionalPropertyTypes": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedIndexedAccess": true,
"noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true,
"allowUnusedLabels": false,
"allowUnreachableCode": false,
"skipLibCheck": true
},
"include": ["./src"],
"exclude": ["node_modules", "./dist"]
}
tsconfig.json 各設定値について補足
"target": "ES2021",
"lib": ["ES2021"],
NodeJSのバージョンに合わせて設定、(現在LTSの)NodeJS v16では ES2021 までサポートしている
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
デコレータを利用する必要がある場合 true にしておく
"rootDir": "./src",
ソースコードを src ディレクトリ内に置くプロジェクトレイアウトの場合はこのようにしておく
"baseUrl": "./src",
"paths": { "~/": ["./"] },
パスエイリアスの設定。どの階層のファイルからも src/ を ~/ と見做して import できるようにしている
"outDir": "./dist",
コンパイルした JS の出力先。
ソースコードと同じディレクトリに混在しないよう dist ディレクトリ配下に出力する設定にする
webpack や esbuild などのバンドルツールを利用する場合は効果がないので注意
"strict": true,
から
"allowUnreachableCode": false,
これらの厳密な構文チェックの設定は最初にすべて有効にしておき、支障がある場合のみ無効化している
各項目のさらに詳しい説明は公式ドキュメントを参照
TypeScriptでの開発中に簡易に実行するためのツールを追加
npm install --save-dev ts-node ts-node-dev tsconfig-paths
ts-node …TSファイルを直接実行できる
ts-node-dev …(サーバプログラム開発などで)実行しながらコード修正時に自動で再起動(watch)させる
tsconfig-paths …実行時にパスエイリアスを解決させる
を大抵の場合TypeScriptのプロジェクトに追加している
以下のように -r tsconfig-paths/register
を渡すことでパスエイリアスを解決させて実行できる
ts-node -r tsconfig-paths/register src/main.ts
ts-node-dev -r tsconfig-paths/register src/main.ts
# パスエイリアスを使用したTSをコンパイルしたJS実行時に
TS_NODE_BASEURL=dist node -r tsconfig-paths/register dist/main.js
package.json の scripts に追加
短いコマンド npm run dev
でメインスクリプトを起動できるよう package.json の scripts に登録しておく
"scripts": {
...
+ "dev": "ts-node-dev -r tsconfig-paths/register src/main.ts",
...
},
フォーマッタ(Prettier)の導入
フォーマッタツールPrettierをプロジェクトに追加する
npm install --save-dev prettier
.prettierrc.js を設定
プロジェクトの規約に沿って設定値を変える
/** @type {import("prettier").Config} */
module.exports = {
printWidth: 120,
tabWidth: 4,
useTabs: false,
semi: true,
singleQuote: false,
quoteProps: "consistent",
jsxSingleQuote: false,
trailingComma: "all",
bracketSpacing: true,
bracketSameLine: false,
arrowParens: "always",
};
各設定項目の補足
/** @type {import("prettier").Config} */
module.exports = {
// 1行の最大長
printWidth: 120,
// インデント幅
tabWidth: 4,
// インデントにスペースを使用
useTabs: false,
// 文末にセミコロンをつける
semi: true,
// 文字列リテラルをダブルクォーテーションで囲む
singleQuote: false,
// オブジェクトリテラルのプロパティ名のクォーテーションに一貫性をもたせる
quoteProps: "consistent",
// JSX属性値をダブルクォーテーションで囲む
jsxSingleQuote: false,
// 配列(オブジェクト)リテラルの最後の要素(プロパティ)の後ろにもカンマを付ける
trailingComma: "all",
// 波括弧の内側にスペースを入れる
bracketSpacing: true,
// 複数行のHTML要素・JSXタグの「>」を単独で新しい行に置く
bracketSameLine: false,
// アロー関数の引数が1つの場合も丸括弧で囲む
arrowParens: "always",
};
※更に詳しい各設定項目の説明は公式ドキュメントを参照
package.json の scripts を追加
チェックのみを行う lint
スクリプトと、訂正を行う fix
スクリプトを package.json に追加しておく
"scripts": {
...
+ "lint": "prettier -l src",
+ "fix": "prettier -w src",
...
}
テストツール (Jest) の導入
Jestをプロジェクトに追加する
あわせて TypeScript に対応させるための ts-jest と型定義も追加
npm install --save-dev jest @types/jest ts-jest
設定ファイル jest.config.js の追加
TypeScript用の設定ファイルを生成する
npx ts-jest config:init
生成された jest.config.js を、
パスエイリアスに対応させるため以下のように修正
const { pathsToModuleNameMapper } = require("ts-jest");
const { loadConfig } = require("tsconfig-paths");
const tsconfig = loadConfig();
/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */
module.exports = {
preset: "ts-jest",
testEnvironment: "node",
// paths
moduleNameMapper: pathsToModuleNameMapper(tsconfig.paths, {
prefix: tsconfig.absoluteBaseUrl,
}),
};
package.json の scripts を追加
npm run test
で実行できるように package.json に追加しておく
(オプションを追加しなければ npx jest
より長くなるのであまり意味がないかもしれない…)
"scripts": {
...
+ "test": "jest",
...
}
コードチェックツール ESLint の導入
ESLintを追加
TypeScriptに対応させるための typescript-eslint
import 順序のルールを追加する eslint-plugin-import
prettier と競合する eslint ルールを無効化する eslint-config-prettier
jest 用のルールを追加する eslint-plugin-jest なども追加する
npm install --save-dev eslint \
@typescript-eslint/parser @typescript-eslint/eslint-plugin \
eslint-plugin-import eslint-import-resolver-typescript \
eslint-config-prettier eslint-plugin-jest
設定ファイル .eslintrc.js を追加
以下のように各プラグインの推奨ルールを extends に入れておき、
プロジェクト開発に合わないものや足りないものを rules で無効化/追加する
/** @type {import("eslint").Linter.Config} */
module.exports = {
env: {
node: true,
commonjs: true,
es2021: true,
jest: true,
},
plugins: [
//
"@typescript-eslint",
"import",
"jest",
],
parser: "@typescript-eslint/parser",
parserOptions: {
project: "./tsconfig.json",
},
settings: {
"import/parsers": {
"@typescript-eslint/parser": [".ts", ".tsx"],
},
"import/resolver": {
typescript: true,
},
},
extends: [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:import/recommended",
"plugin:import/typescript",
"plugin:jest/recommended",
"prettier",
],
rules: {},
};
rulesに追加する一例
ESLintrules追加の例
// 単一行のifブロックの波括弧省略禁止
"curly": "error",
// ifブロックで必ずreturnする場合のelse記述禁止
"no-else-return": "error",
// 厳密比較の強制
"eqeqeq": "error",
// 関数の最大行数(トップレベルの関数除く)
"max-statements": ["error", 40, { ignoreTopLevelFunctions: true }],
// ひとつの var/const/letで複数の変数を宣言することを禁止
"one-var": ["error", "never"],
import のルール追加例
// import文をファイルの先頭に置く
"import/first": "error",
// import 記述順のルール
"import/order": ["error", {
// アルファベット順に並べる
"alphabetize": { order: "asc", caseInsensitive: false },
// グループごとに並べる NodeJSビルトインモジュール→外部モジュール→内部モジュール→親ディレクトリ
"groups": ["builtin", "external", "internal", "parent", "sibling"],
// グループ間に空行を入れる
"newlines-between": "always",
}],
package.json の scripts を修正
npm run lint
で ESLint によるチェックも実行されるようにする
npm run fix
で ESLint ルール違反の訂正(自動でできるものに限る)も実行されるようにする
"scripts": {
...
- "lint": "prettier -l src",
+ "lint": "prettier -l src && eslint src",
- "fix": "prettier -w src",
+ "fix": "prettier -w src && eslint src --fix",
...
}