Typescript・Prettier・ESLint・JestでTypescriptの実行環境を0から構築してみた
内容
Typescriptでプロジェクトを構築するために、prettier・ESLint・jestが導入されたTypescriptプロジェクトを0から作成します。
今回はフロントエンドではなく、Node.jsの実行環境を作成します。
完成品は、このリポジトリに配置しています。
準備
以下を準備していきます。
- npm環境の構築
- typescript
- prettier
- ESLint
- jest
npm環境の構築
jsのプロジェクトを作成するということで、npm環境の構築から始めます。プロジェクトを作成する予定のディレクトリの中で、以下のコマンドを実行
$ npm init -y
ディレクトリにpackage.json
というファイルが作成されます。
これで、npm環境の構築は完了し、このプロジェクトで、npm moduleが使用できるようになりました。
typescript環境の構築
次に、typescript環境を構築します。
$ npm i typescript --save-dev
$ touch tsconfig.json
tsconfig.json
{
"compilerOptions": {
"target": "es2022",
"module": "commonjs",
"rootDir": "./src",
"outDir": "./dist",
"strict": true,
"forceConsistentCasingInFileNames": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedIndexedAccess": true,
"noPropertyAccessFromIndexSignature": true
},
"include": ["src/**/*"]
}
最低限必要そうな設定値だけを設定しておきました。自分の好みに合わせて変更してください。
Typescriptでファイルをコンパイルする準備が整いました。テストでファイルをコンパイルします。
# src -> typescriptを配置するディレクトリ
# dist -> コンパイルされた後のjavascriptが出力されるディレクトリ
$ mkdir src dist
$ touch src/main.ts
$ vi src/main.ts
main.ts
export function hello(name: string): string {
return `Hello ${name}! `;
}
このsrc/main.ts
ファイルをdist
ディレクトリ配下にコンパイルしていきます。
$ npx tsc
$ cat dist/main.js
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.hello = void 0;
function hello(name) {
return `Hello ${name}! `;
}
exports.hello = hello;
正常にTypescriptでコンパイルされました。最後に、npmスクリプトにtypescriptのbuildコマンドを追加して、Typescriptの設定は完了です。
package.json
{
"name": "ts-template",
"version": "1.0.0",
"description": "Typescript Template",
"main": "main.js",
"scripts": {
// ここを追加
"build": "tsc"
},
"repository": {
"type": "git",
"url": "git+https://github.com/rara-tan/ts-template.git"
},
"keywords": [
"actions",
"github",
"node",
"typescript"
],
"author": "yossy",
"license": "ISC",
"bugs": {
"url": "https://github.com/rara-tan/ts-template/issues"
},
"homepage": "https://github.com/rara-tan/ts-template#readme",
"devDependencies": {
"typescript": "^5.0.3"
}
}
上記のように、package.jsonにbuild
コマンドを追加して、ターミナルから実行します。
$ npm run build
> gh-actions-ts-template@1.0.0 build
> tsc
$
正常に動作していることを確認したら、完了です。
ESLintの導入
次にESLintを導入します。ESLintはTypescriptが事前に設定したルール通りに書かれているかどうかを判定してくれるツールで、プロジェクト全体を通して、統一的なソースの記述を強制してくれるツールです。
$ npm install --save-dev eslint
Ok to proceed? (y) -> y
# インタラクティブに質問が表示されるので、以下のように回答していきます。
How would you like to use ESLint? -> To check syntax, find problems, and enforce code style
What type of modules does your project use? -> CommonJS (require/exports)
Which framework does your project use? -> None of these
Does your project use TypeScript? -> Yes
Where does your code run? -> Node
How would you like to define a style for your project? -> Use a popular style guide
Which style guide do you want to follow? -> Standard
What format do you want your config file to be in? -> JavaScript
Would you like to install them now? -> Yes
Which package manager do you want to use? -> npm
以上のように回答していくと、以下のような.eslintrc.js
ファイルが生成されます。
.eslintrc.js
module.exports = {
env: {
browser: true,
commonjs: true,
es2021: true
},
extends: 'standard-with-typescript',
overrides: [
],
parserOptions: {
ecmaVersion: 'latest'
},
rules: {
}
}
このまま、ESLintを実行すると以下のエラーが発生します。
Error: Error while loading rule '@typescript-eslint/dot-notation': You have used a rule which requires parserServices to be generated. You must therefore provide a value for the "parserOptions.project" property for @typescript-eslint/parser.
そのため、.eslintrc.js
ファイルに、tsconfig
ファイルを明示的に指定します。
.eslintrc.js
module.exports = {
env: {
browser: true,
commonjs: true,
es2021: true
},
extends: 'standard-with-typescript',
overrides: [
],
parserOptions: {
ecmaVersion: 'latest',
// ここを追加
project: './tsconfig.json',
},
rules: {
}
}
これでESLintを実行する準備が整いました。以下のコマンドを実行して、先ほど作成したtsファイルを検証します。
$ npx eslint src/main.ts
/Users/username/Projects/ts-template/src/main.ts
1:22 error Missing space before function parentheses @typescript-eslint/space-before-function-paren
2:27 error Extra semicolon @typescript-eslint/semi
4:1 error Too many blank lines at the end of file. Max of 0 allowed no-multiple-empty-lines
✖ 3 problems (3 errors, 0 warnings)
3 errors and 0 warnings potentially fixable with the `--fix` option.
いくつかのエラーが発生しました。Lintチェックのエラーを解消するため、--fix
オプションをつけて、再度 eslint
を実行します。
$ npx eslint src/main.ts --fix
これでESLintで設定したルール通りにtypescriptファイルが修正されました。(ファイルを確認すると、編集されていることが確認できます)
ここまでで、ESLintとTypescriptの導入は完了です。この記事と同じような手順を踏んだ場合、以下のようなファイル構成になっていると思います。
$ tree -a
.
├── .eslintrc.js
├── dist
│ └── main.js
├── package-lock.json
├── package.json
├── src
│ └── main.ts
└── tsconfig.json
最後に、npmスクリプトからESLintが実行できるように、package.jsonにlint系のコマンドを追加しておきます。
{
"name": "ts-template",
...
"scripts": {
"build": "tsc",
// 追加
"lint": "eslint src/**/*.ts",
"lint-fix": "eslint src/**/*.ts --fix"
},
...
}
prettierの導入
次にjavascriptのフォーマットツールprettierを導入します。
prettierを導入すると設定したフォーマット通りにTypescriptのソースが記述されているかどうかをチェックしてくれます。(自動で整形もしてくれます)
$ npm install --save-dev prettier eslint-config-prettier
次にESLintの設定ファイルにprettier
に関する記述を追加します。
.eslintrc.js
module.exports = {
env: {
browser: true,
commonjs: true,
es2021: true
},
extends: [
'standard-with-typescript',
// ここを追加
'prettier',
],
overrides: [
],
parserOptions: {
ecmaVersion: 'latest',
project: './tsconfig.json',
},
rules: {
}
}
次に、prettierの設定ファイル.prettierrc.json
を追加して、prettierのルールを設定します。
$ touch .prettierrc.json
.prettierrc.json
{
"printWidth": 80,
"tabWidth": 2,
"useTabs": false,
"semi": false,
"singleQuote": true,
"trailingComma": "none",
"bracketSpacing": false,
"arrowParens": "avoid"
}
これでprettierを利用する準備が整いました。試しに、src/main.ts
ファイルにフォーマットを実行します。
$ npx prettier --write src/main.ts
src/main.ts 76ms
自動フォーマットが実行されました。わざと、セミコロンを追加したら、スペースの数を変更してファイルのフォーマットを崩してから実行すると、自動で整形されているのがわかりやすいかと思います。
prettierもESLintやTypescriptと同様に、npm scriptで実行できるように、package.jsonを編集します。
package.json
{
"name": "ts-template",
...
"scripts": {
"build": "tsc",
"lint": "eslint src/**/*.ts",
"lint-fix": "eslint src/**/*.ts --fix",
// ここを追加
"format": "prettier --write src/**/*.ts",
"format-check": "prettier --check src/**/*.ts"
},
...
}
これで、prettierの追加が完了しました。
ここまでで、ESLintとTypescriptとprettierの導入は完了です。この記事と同じような手順を踏んだ場合、以下のようなファイル構成になっていると思います。
$ tree -a
.
├── .eslintrc.js
├── .prettierrc.json
├── dist
│ └── main.js
├── package-lock.json
├── package.json
├── src
│ └── main.ts
└── tsconfig.json
Jestの導入
最後にjavascript用テストツールJestを導入します。
まずはパッケージのインストールから。
$ npm install --save-dev jest ts-jest @types/jest
今回は、ts-jest
を使って、Typescript環境でJestが使えるようにしていきます。以下のコマンドにて設定ファイルを作成します。
$ npx ts-jest config:init
Jest configuration written to "/Users/user-name/Projects/ts-template/jest.config.js".
上記コマンドで作成されたjest.config.js
ファイルに、rootsプロパティを追加して、テスト対象のファイルがどのディレクトリに含まれているかを追記します。
jest.confing.js
/** @type {import('ts-jest').JestConfigWithTsJest} */
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
roots: ["<rootDir>/tests"],
};
これで、tests
ディレクトリ配下にテストファイルを配置すれば、typescriptファイルのテストが実行できるようになりました。src/main.ts
のhello
関数をテストするテストファイルを作成します。
$ touch tests/main.test.ts
tests/main.test.ts
import {hello} from '../src/main'
describe('hello test', () => {
test('OK', async () => {
const res = hello("yossy")
expect(res).toBe("Hello yossy!")
})
})
そして、jestの実行コマンドをターミナルから実行します。
$ npx jest
PASS tests/main.test.ts
hello test
✓ OK (1 ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 0.559 s, estimated 1 s
Ran all test suites.
正常にテストが実行されているのを確認できます!
これでjestを使って、Typescriptのファイルをテストすることができるようになりました。
ここまで、記事通りに操作を行なっていれば、以下のようなディレクトリ構成になっているはずです。
$ tree -a
.
├── .eslintrc.js
├── .prettierrc.json
├── dist
│ └── main.js
├── jest.config.js
├── package-lock.json
├── package.json
├── src
│ └── main.ts
├── tests
│ └── main.test.ts
└── tsconfig.json
jestもprettierやESLintと同様、npm scriptに追加しておきましょう。
package.json
{
"name": "ts-template",
...
"scripts": {
"build": "tsc",
"lint": "eslint src/**/*.ts",
"lint-fix": "eslint src/**/*.ts --fix",
"format": "prettier --write src/**/*.ts",
"format-check": "prettier --check src/**/*.ts",
"test": "jest"
},
...
}
jestにカバレッジを追加する
最後に、jestにカバレッジを追加します。カバレッジを追加すると、テスト対象のディレクトリの中のファイルのうち何パーセントがテストされているかを確認できるようになります。
jestにカバレッジを追加するのは簡単で、以下のように、jest.config.js
ファイルにカバレッジに関する記述を追加します。
jest.config.js
/** @type {import('ts-jest').JestConfigWithTsJest} */
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
roots: ["<rootDir>/tests"],
collectCoverage: true,
collectCoverageFrom: [
"<rootDir>/src/**/*.ts",
"!**/node_modules/**",
],
};
この状態で、npm run test
を実行すると、テスト結果にカバレッジ表示が追加されているのを確認できるかと思います。
$ npm run test
PASS tests/main.test.ts
hello test
✓ OK (1 ms)
----------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
----------|---------|----------|---------|---------|-------------------
All files | 100 | 100 | 100 | 100 |
main.ts | 100 | 100 | 100 | 100 |
----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 0.576 s, estimated 1 s
Ran all test suites.
※カバレッジ結果を保存する、coverageディレクトリが自動で作成されます。
まとめ
これにて、
- typescript
- ESLint
- prettier
- Jest
が導入されたプロジェクトの雛形を作成することができました。
githubに今回の雛形の完成系を置いておきます。
Discussion