TypeScript / ESLint 設定
TypeScript 設定
共有設定パッケージ
TypeScript の基本設定は @monorepo/typescript-config パッケージで一元管理しています。
// packages/typescript-config/tsconfig.json
{
"$schema": "https://json.schemastore.org/tsconfig",
"compilerOptions": {
"allowSyntheticDefaultImports": true,
"declaration": true,
"declarationMap": true,
"emitDecoratorMetadata": true,
"esModuleInterop": true,
"experimentalDecorators": true,
"forceConsistentCasingInFileNames": true,
"isolatedModules": true,
"module": "esnext",
"moduleResolution": "bundler",
"removeComments": false,
"skipLibCheck": true,
"sourceMap": true,
"strict": true,
"types": ["node"]
}
}
moduleResolution: "bundler" の意味
moduleResolution: "bundler" は TypeScript 5.0 で導入されたモジュール解決戦略です。
| 設定値 | 説明 |
|---|---|
node |
Node.js の CommonJS 解決アルゴリズム |
node16 |
Node.js の ESModule 対応解決(拡張子必須) |
bundler |
バンドラー向け(拡張子省略可、ESModule 対応) |
esbuild を使用する場合、bundler が最適です。
- ✅ 拡張子を省略してインポートできる
- ✅ ESModule のセマンティクスに準拠
- ✅ パスエイリアスとの相性が良い
// moduleResolution: "bundler" では OK
import { UserService } from './user.service';
// moduleResolution: "node16" では NG(拡張子必須)
import { UserService } from './user.service.js';
パスエイリアスの設定
サーバーアプリケーションでは、パスエイリアスを設定してインポートを簡潔にしています。
// apps/server/tsconfig.json
{
"extends": "@monorepo/typescript-config",
"compilerOptions": {
"baseUrl": "./",
"paths": {
"$domains/*": ["src/domains/*"],
"$shared/*": ["src/shared/*"]
},
"target": "ES2023"
},
"references": [{ "path": "./tsconfig.build.json" }, { "path": "./tsconfig.spec.json" }]
}
ポイント:
$domains/と$shared/で明示的にパスを区別@プレフィックスを避けることで、npm パッケージとの混同を防止- Project References で型チェックを分離
Project References による分離
Project References を使用して、ビルド用とテスト用の設定を分離しています。
apps/server/
├── tsconfig.json # ベース設定 + references
├── tsconfig.build.json # 本番ビルド用
└── tsconfig.spec.json # テスト用
これにより、VS Code のエディタ内型チェックと CLI の tsc コマンドで同じ挙動を実現します。
ESLint Flat Config
共有設定パッケージ
ESLint の設定も @monorepo/eslint-config パッケージで一元管理しています。ESLint 9.0 で導入された Flat Config 形式を使用しています。
// packages/eslint-config/index.js
import js from '@eslint/js';
import eslintConfigPrettier from 'eslint-config-prettier';
import boundariesPlugin from 'eslint-plugin-boundaries';
import importPlugin from 'eslint-plugin-import';
import turboPlugin from 'eslint-plugin-turbo';
import tseslint from 'typescript-eslint';
export const baseConfig = [
{
ignores: ['dist/**', 'build/**', 'node_modules/**', 'eslint.config.mjs'],
},
js.configs.recommended,
eslintConfigPrettier,
...tseslint.configs.recommended,
{
settings: {
'import/resolver': {
typescript: {
project: ['apps/*/tsconfig.json', 'apps/*/tsconfig.spec.json', 'packages/*/tsconfig.json'],
},
},
'boundaries/elements': [
{ type: 'domains', pattern: '$domains/**' },
{ type: 'modules', pattern: '$modules/**' },
{ type: 'shared', pattern: '$shared/**' },
],
},
plugins: {
boundaries: boundariesPlugin,
import: importPlugin,
turbo: turboPlugin,
},
rules: {
// ... ルール設定
},
},
];
Flat Config の利点
従来の .eslintrc 形式と比較して、以下の利点があります。
| 観点 | .eslintrc 形式 | Flat Config |
|---|---|---|
| 設定形式 | JSON/YAML/JS | JavaScript のみ |
| 型安全性 | なし | TypeScript 対応 |
| 設定の継承 | extends で複雑化 | 配列のスプレッドでシンプル |
| プラグイン読み込み | 文字列で指定 | import で明示的 |
// Flat Config: 配列のスプレッドで継承
export default [
...baseConfig,
{
// パッケージ固有の設定
},
];
eslint-plugin-boundaries によるアーキテクチャ検証
eslint-plugin-boundaries を使用して、アーキテクチャの依存関係を自動でチェックしています。
'boundaries/element-types': [
'error',
{
default: 'allow',
rules: [
{
from: 'modules',
disallow: ['domains'],
message: 'Modules should not depend on domains.',
},
{
from: 'shared',
disallow: ['domains', 'modules'],
message: 'Shared should not depend on domains or modules.',
},
],
},
],
依存関係のルール:
domains/ ──参照可→ modules/ ──参照可→ shared/
│ │
└──参照可─────────┘
※ 逆方向の参照は禁止
| 参照元 | domains | modules | shared |
|---|---|---|---|
domains/ |
- | ✅ | ✅ |
modules/ |
❌ | - | ✅ |
shared/ |
❌ | ❌ | - |
これにより、依存関係の逆転を防ぎ、アーキテクチャの整合性を維持します。
import/order による整列されたインポート順序
インポート文を自動で整列し、一貫したコードスタイルを維持します。
'import/order': [
'error',
{
groups: [
['builtin', 'external'],
['internal', 'parent', 'sibling', 'index', 'object'],
'type',
],
pathGroups: [
{
pattern: '${domains,modules,shared}/**',
group: 'internal',
},
],
alphabetize: {
order: 'asc',
caseInsensitive: true,
orderImportKind: 'asc',
},
'newlines-between': 'always',
},
],
整列結果:
// 1. builtin / external(Node.js 標準 + npm パッケージ)
import { Injectable } from '@nestjs/common';
import { PrismaClient } from '@prisma/client';
// 2. internal(パスエイリアス)
import { PrismaAdapter } from '$shared/adapters';
import { UserRepository } from '$domains/user/repositories';
// 3. type(型インポート)
import type { User } from '$prisma/client';
サーバー固有の ESLint 設定
サーバーアプリケーションでは、共有設定を拡張して NestJS 向けの調整を行っています。
// apps/server/eslint.config.mjs
import { baseConfig } from '@monorepo/eslint-config';
export default [
...baseConfig,
{
ignores: ['*.config.cjs', '*.config.mjs'],
},
{
files: ['src/**/*.ts'],
languageOptions: {
parserOptions: {
projectService: true,
tsconfigRootDir: import.meta.dirname,
},
},
rules: {
'@typescript-eslint/no-explicit-any': 'off',
},
},
];
ポイント:
projectService: trueで TypeScript Language Service と連携@typescript-eslint/no-explicit-any: 'off'で NestJS の any 使用を許容
まとめ
モダンな TypeScript / ESLint 設定により、以下を実現しています。
- moduleResolution: "bundler" - esbuild との相性が良いモジュール解決
- Project References - VS Code と tsc の挙動一致
- ESLint Flat Config - シンプルで型安全な設定
- eslint-plugin-boundaries - アーキテクチャの自動検証
- import/order - 一貫したインポート順序
次のページでは、アーキテクチャ設計(Vertical Slice Architecture と CQRS)について解説します。