💬

Typescript・Prettier・ESLint・JestでTypescriptの実行環境を0から構築してみた

2023/04/02に公開

内容

Typescriptでプロジェクトを構築するために、prettier・ESLint・jestが導入されたTypescriptプロジェクトを0から作成します。

今回はフロントエンドではなく、Node.jsの実行環境を作成します。

完成品は、このリポジトリに配置しています。

https://github.com/rara-tan/ts-template

準備

以下を準備していきます。

  • 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.tshello関数をテストするテストファイルを作成します。

$ 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に今回の雛形の完成系を置いておきます。

https://github.com/rara-tan/ts-template

Discussion