🍣

tsconfig.json の resolveJsonModule

に公開

概要

TypeScript はデフォルトで JSON ファイルの型検証をサポートしていません。よって、JSON ファイルをインポートするとエラーが出ます。resolveJsonModuletrue にすると、TypeScript は .json の拡張子を含むモジュールをインポートできるようになります。

resolveJsonModulefalse がデフォルトです。

Next.js の設定考察

create next-app で作成した Next.js プロジェクトで resolveJsonModuletrue が設定されています。JSON ファイルの型検証をサポートするため、特段理由がなければ resolveJsonModule を有効化しておくことをお勧めします。また、仮に false を設定しても、Next.js によって強制的に resolveJsonModule が有効化されます。

tsconfig.json
{
  "compilerOptions": {
    "resolveJsonModule": true,
  },
}

参考

公式の説明はこちらです。

https://www.typescriptlang.org/ja/tsconfig#resolveJsonModule

以下が作業リポジトリです。

https://github.com/hayato94087/tsconfig-resolvejsonmodule

https://github.com/hayato94087/next-tsconfig-resolvejsonmodule

この記事の内容

この記事では resolveJsonModulefalse の場合と true の場合で動作を確認します。Node.js & TypeScript のプロジェクトと Next.js のプロジェクトで動作確認を行います。

Node.js & TypeScriptのプロジェクトで動作確認

TypeScript の簡易プロジェクトを作成し、resolveJsonModulefalse の場合と true の場合で動作を確認します。

事前環境の構築

動作を作業するための Node.js & TypeScript のプロジェクトを作成します。長いので、折り畳んでおきます。

新規プロジェクト作成と初期環境構築の手順詳細

TypeScript の簡易プロジェクトを作成します。

まず、package.json を作成します。

$ mkdir -p tsconfig-resolvejsonmodule
$ cd tsconfig-resolvejsonmodule
$ pnpm init

下記で package.json を上書きします。ポイントは scripts に 3 つのスクリプトを追加しています。typecheck で型をチェックし、dev でローカルで動作確認、build でトランスパイルします。

package.json
{
  "name": "tsconfig-resolvejsonmodule",
  "version": "1.0.0",
  "description": "",
  "main": "index.ts",
  "scripts": {
    "typecheck": "tsc --noEmit",
    "dev": "ts-node index.ts",
    "build": "tsc"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

TypeScript をインストールします。

$ pnpm install -D typescript ts-node

tsconfig.json を作成します。

$ npx tsc --init

tsconfig.json を作成します。

tsconfig.json
{
  "compilerOptions": {
    "target": "es2015",
    "module": "commonjs",
    "sourceMap": true,
    "outDir": "./dist",
    "strict": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "esModuleInterop":true
  },
  "include": ["**/*.ts","**/*.js"],
  "exclude": ["node_modules", "dist"]
}

git を初期化します。

$ git init

.gitignore を作成します。

$ touch .gitignore
.gitignore
node_modules

コミットします。

$ git add .
$ git commit -m "feat:初期コミット"

resolveJsonModulefalse の場合

resolveJsonModulefalse の場合、TypeScript は JSON ファイルを扱うことができます。

tsconfig.json を作成します。resolveJsonModule を無効化します。

tsconfig.json
{
  "compilerOptions": {
    "target": "es2015",
    "module": "commonjs",
    "sourceMap": true,
    "outDir": "./dist",
    "strict": true,
    "skipLibCheck": true,
+   "resolveJsonModule": false,
    "forceConsistentCasingInFileNames": true,
    "esModuleInterop":true
  },
  "include": ["**/*.ts","**/*.js"],
  "exclude": ["node_modules", "dist"]
}

読み込む JSON ファイル settings.json を作成し TypeScript ファイルの index.ts から JavaScript を読み込んで利用します。

$ touch index.ts settings.json
settings.json
{
  "message": "hello world"
}
index.ts
import settings from "./settings.json";

console.log(settings);

型をチェックします。resolveJsonModule が無効化されており、TypeScript で JSON が扱えないためエラーとなります。

$ pnpm run typecheck

index.ts:1:22 - error TS2732: Cannot find module './settings.json'. Consider using '--resolveJsonModule' to import module with '.json' extension.

1 import settings from "./settings.json";
                       ~~~~~~~~~~~~~~~~~

Found 1 error in index.ts:1

ローカルで動作確認します。同様のエラーが出ます。

$ pnpm run dev

/Users/hayato94087/Private/tsconfig-resolvejsonmodule/node_modules/.pnpm/ts-node@10.9.2_@types+node@20.12.3_typescript@5.4.3/node_modules/ts-node/src/index.ts:859
    return new TSError(diagnosticText, diagnosticCodes, diagnostics);
           ^
TSError: Unable to compile TypeScript:
index.ts:1:22 - error TS2732: Cannot find module './settings.json'. Consider using '--resolveJsonModule' to import module with '.json' extension.

1 import settings from "./settings.json";
                       ~~~~~~~~~~~~~~~~~

    at createTSError (/Users/hayato94087/Private/tsconfig-resolvejsonmodule/node_modules/.pnpm/ts-node@10.9.2_@types+node@20.12.3_typescript@5.4.3/node_modules/ts-node/src/index.ts:859:12)
    at reportTSError (/Users/hayato94087/Private/tsconfig-resolvejsonmodule/node_modules/.pnpm/ts-node@10.9.2_@types+node@20.12.3_typescript@5.4.3/node_modules/ts-node/src/index.ts:863:19)
    at getOutput (/Users/hayato94087/Private/tsconfig-resolvejsonmodule/node_modules/.pnpm/ts-node@10.9.2_@types+node@20.12.3_typescript@5.4.3/node_modules/ts-node/src/index.ts:1077:36)
    at Object.compile (/Users/hayato94087/Private/tsconfig-resolvejsonmodule/node_modules/.pnpm/ts-node@10.9.2_@types+node@20.12.3_typescript@5.4.3/node_modules/ts-node/src/index.ts:1433:41)
    at Module.m._compile (/Users/hayato94087/Private/tsconfig-resolvejsonmodule/node_modules/.pnpm/ts-node@10.9.2_@types+node@20.12.3_typescript@5.4.3/node_modules/ts-node/src/index.ts:1617:30)
    at Module._extensions..js (node:internal/modules/cjs/loader:1329:10)
    at Object.require.extensions.<computed> [as .ts] (/Users/hayato94087/Private/tsconfig-resolvejsonmodule/node_modules/.pnpm/ts-node@10.9.2_@types+node@20.12.3_typescript@5.4.3/node_modules/ts-node/src/index.ts:1621:12)
    at Module.load (node:internal/modules/cjs/loader:1133:32)
    at Function.Module._load (node:internal/modules/cjs/loader:972:12)
    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:83:12) {
  diagnosticCodes: [ 2732 ]
}

コミットします。

$ git add .
$ git commit -m "feat: resolveJsonModuleを無効化しエラーを確認"

resolveJsonModuletrue の場合

resolveJsonModuletrue の場合、TypeScript は JSON ファイルを扱うことができます。resolveJsonModule を有効化することで先程のエラーが解消することを確認します。

tsconfig.json を修正します。resolveJsonModule を有効化します。

tsconfig.json
{
  "compilerOptions": {
    "target": "es2015",
    "module": "commonjs",
    "sourceMap": true,
    "outDir": "./dist",
    "strict": true,
    "skipLibCheck": true,
-   "resolveJsonModule": false,
+   "resolveJsonModule": true,
    "forceConsistentCasingInFileNames": true,
    "esModuleInterop":true
  },
  "include": ["**/*.ts","**/*.js"],
  "exclude": ["node_modules", "dist"]
}

型チェックします。resolveJsonModule を有効化したため、TypeScript で JSON ファイルを扱うことができます。エラーは出ません。

$ pnpm run typecheck

ローカルで動作確認します。問題無く動作します。

$ pnpm run dev

{ message: 'hello world' }

コミットします。

$ git add .
$ git commit -m "feat: resolveJsonModuleを有効化しエラーがないことを確認"

Next.jsのプロジェクトで動作確認

Next.js のプロジェクトを作成し、resolveJsonModulefalse の場合と true の場合で動作を確認します。

事前環境の構築

動作を作業するための Next.js プロジェクトを作成します。長いので、折り畳んでおきます。

新規プロジェクト作成と初期環境構築の手順詳細

プロジェクトを作成

create next-app@latestでプロジェクトを作成します。

$ pnpm create next-app@latest next-tsconfig-resolvejsonmodule --typescript --eslint --import-alias "@/*" --src-dir --use-pnpm --tailwind --app
$ cd next-tsconfig-resolvejsonmodule

Peer Dependenciesの警告を解消

Peer dependenciesの警告が出ている場合は、pnpm installを実行し、警告を解消します。

 WARN  Issues with peer dependencies found
.
├─┬ autoprefixer 10.0.1
 └── unmet peer postcss@^8.1.0: found 8.0.0
├─┬ tailwindcss 3.3.0
 ├── unmet peer postcss@^8.0.9: found 8.0.0
 ├─┬ postcss-js 4.0.1
 └── unmet peer postcss@^8.4.21: found 8.0.0
 ├─┬ postcss-load-config 3.1.4
 └── unmet peer postcss@>=8.0.9: found 8.0.0
 └─┬ postcss-nested 6.0.0
   └── unmet peer postcss@^8.2.14: found 8.0.0
└─┬ next 14.0.4
  ├── unmet peer react@^18.2.0: found 18.0.0
  └── unmet peer react-dom@^18.2.0: found 18.0.0

以下を実行することで警告が解消されます。

$ pnpm i -D postcss@latest react@^18.2.0 react-dom@^18.2.0

不要な設定を削除し、プロジェクトを初期化します。

styles

CSSなどを管理するstylesディレクトリを作成します。globals.cssを移動します。

$ mkdir -p src/styles
$ mv src/app/globals.css src/styles/globals.css

globals.cssの内容を以下のように上書きします。

src/styles/globals.css
@tailwind base;
@tailwind components;
@tailwind utilities;

初期ページ

app/page.tsxを上書きします。

src/app/page.tsx
import { type FC } from "react";

const Home: FC = () => {
  return (
    <div className="">
      <div className="text-lg font-bold">Home</div>
      <div>
        <span className="text-blue-500">Hello</span>
        <span className="text-red-500">World</span>
      </div>
    </div>
  );
};

export default Home;

レイアウト

app/layout.tsxを上書きします。

src/app/layout.tsx
import "@/styles/globals.css";
import { type FC } from "react";
type RootLayoutProps = {
  children: React.ReactNode;
};

export const metadata = {
  title: "Sample",
  description: "Generated by create next app",
};

const RootLayout: FC<RootLayoutProps> = (props) => {
  return (
    <html lang="ja">
      <body className="">{props.children}</body>
    </html>
  );
};

export default RootLayout;

TailwindCSSの設定

TailwindCSSの設定(tailwind.config.ts)を上書きします。

tailwind.config.ts
import type { Config } from 'tailwindcss'

const config: Config = {
  content: [
    './src/pages/**/*.{js,ts,jsx,tsx,mdx}',
    './src/components/**/*.{js,ts,jsx,tsx,mdx}',
    './src/app/**/*.{js,ts,jsx,tsx,mdx}',
  ],
  plugins: [],
}
export default config

TypeScriptの設定

TypeScriptの初期設定はこちらです。

tsconfig.json
{
  "compilerOptions": {
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true,
    "skipLibCheck": true,
    "strict": true,
    "noEmit": true,
    "esModuleInterop": true,
    "module": "esnext",
    "moduleResolution": "bundler",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "jsx": "preserve",
    "incremental": true,
    "plugins": [
      {
        "name": "next"
      }
    ],
    "paths": {
      "@/*": ["./src/*"]
    }
  },
  "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
  "exclude": ["node_modules"]
}

スクリプトを追加

型チェックのスクリプトを追加します。

package.json
{
  "name": "next-tsconfig-resolvejsonmodule",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint",
+   "typecheck": "tsc --noEmit"
  },
  "dependencies": {
    "next": "14.1.4"
  },
  "devDependencies": {
    "@types/node": "^20",
    "@types/react": "^18",
    "@types/react-dom": "^18",
    "autoprefixer": "^10.0.1",
    "eslint": "^8",
    "eslint-config-next": "14.1.4",
    "postcss": "^8.4.38",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "tailwindcss": "^3.3.0",
    "typescript": "^5"
  }
}

動作確認

ローカルで動作確認します。

$ pnpm run dev

コミットして作業結果を保存しておきます。

$ git add .
$ git commit -m "feat:新規にプロジェクトを作成し, 作業環境を構築"

resolveJsonModulefalse の場合

resolveJsonModulefalse の場合、TypeScript は JSON ファイルを扱うことができません。

resolveJsonModule を無効化します。checkJs もあわせて無効化します。

tsconfig.json
{
  "compilerOptions": {
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true,
    "skipLibCheck": true,
    "strict": true,
    "noEmit": true,
    "esModuleInterop": true,
    "module": "esnext",
    "moduleResolution": "bundler",
-   "resolveJsonModule": true,
+   "resolveJsonModule": false,
    "isolatedModules": true,
    "jsx": "preserve",
    "incremental": true,
    "plugins": [
      {
        "name": "next"
      }
    ],
    "paths": {
      "@/*": ["./src/*"]
    }
  },
  "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
  "exclude": ["node_modules"]
}

incrementaltrue の場合、設定変更がうまく反映されないため tsconfig.tsbuildinfo を削除します。

$ rm -rf tsconfig.tsbuildinfo

JSON ファイル settings.json を作成し page.tsx から読み込みます。

JavaScript の export を含むファイルを追加します。

$ mkdir -p src/lib
$ touch src/lib/settings.json
src/lib/settings.json
{
  "message": "hello world"
}

page.tsx に JavaScript のファイルをインポートする文を追加します。

src/app/page.tsx
import { type FC } from "react";
+import settings from "@/lib/settings.json";

const Home: FC = () => {
+ console.log(settings);
  return (
    <div className="">
      <div className="text-lg font-bold">Home</div>
      <div>
        <span className="text-blue-500">Hello</span>
        <span className="text-red-500">World</span>
      </div>
    </div>
  );
};

export default Home;

型をチェックします。エラーが出ます。

$ pnpm run typecheck

src/app/page.tsx:2:22 - error TS2732: Cannot find module '@/lib/settings.json'. Consider using '--resolveJsonModule' to import module with '.json' extension.

2 import settings from "@/lib/settings.json";
                       ~~~~~~~~~~~~~~~~~~~~~

Found 1 error in src/app/page.tsx:2

なお、Next.js では next build, next dev を実行することで、resolveJsonModule が自動的に有効化されます。

next dev を実行すると、resolveJsonModule が有効化される旨のメッセージが表示されます。

$ pnpm run dev

 Next.js 14.1.4
   - Local:        http://localhost:3000


   We detected TypeScript in your project and reconfigured your tsconfig.json file for you. Strict-mode is set to false by default.
   The following mandatory changes were made to your tsconfig.json:

        - resolveJsonModule was set to true (to match webpack resolution)

 Ready in 2.4s
 Compiling / ...
 Compiled / in 3.3s (456 modules)
{ message: 'hello world' }
 Compiled in 318ms (220 modules)
 Compiling /favicon.ico ...

tsconfig.jsonresolveJsonModule を再度無効化します。

tsconfig.json
{
  "compilerOptions": {
    "lib": [
      "dom",
      "dom.iterable",
      "esnext"
    ],
    "allowJs": true,
    "skipLibCheck": true,
    "strict": true,
    "noEmit": true,
    "esModuleInterop": true,
    "module": "esnext",
    "moduleResolution": "bundler",
-   "resolveJsonModule": true,
+   "resolveJsonModule": false,
    "isolatedModules": true,
    "jsx": "preserve",
    "incremental": true,
    "plugins": [
      {
        "name": "next"
      }
    ],
    "paths": {
      "@/*": [
        "./src/*"
      ]
    }
  },
  "include": [
    "next-env.d.ts",
    "**/*.ts",
    "**/*.tsx",
    ".next/types/**/*.ts"
  ],
  "exclude": [
    "node_modules"
  ]
}

incrementaltrue の場合、設定変更がうまく反映されないため tsconfig.tsbuildinfo を削除します。

$ rm -rf tsconfig.tsbuildinfo

next build を実行すると、resolveJsonModule が京瀬的に有効化される旨のメッセージが表示されます。

$ pnpm run build

 Next.js 14.1.4

   Creating an optimized production build ...
 Compiled successfully
   Linting and checking validity of types  ..
   We detected TypeScript in your project and reconfigured your tsconfig.json file for you. Strict-mode is set to false by default.
   The following mandatory changes were made to your tsconfig.json:

        - resolveJsonModule was set to true (to match webpack resolution)

 Linting and checking validity of types    
 Collecting page data    
   Generating static pages (0/5)  [=   ]{ message: 'hello world' }
   Generating static pages (1/5)  [==  ] 
 Generating static pages (5/5) 
 Collecting build traces    
 Finalizing page optimization    

Route (app)                              Size     First Load JS
 /                                    137 B          84.4 kB
 /_not-found                          885 B          85.2 kB
+ First Load JS shared by all            84.3 kB
 chunks/672-f5652b77b66c42f3.js       29 kB
 chunks/90234aad-523d1b31770fe8fa.js  53.4 kB
 other shared chunks (total)          1.86 kB

  (Static)  prerendered as static content

よって、結論としては Next.js によって勝手に resolveJsonModule が有効化されるため、resolveJsonModuletrue を設定しておくのが良いです。有効化するかどうかは特に気にする必要がないということです。

コミットします。

$ git add .
$ git commit -m "feat:resolveJsonModuleを無効化しエラーを確認"

Next.jsの設定考察

create next-app で作成した Next.js プロジェクトで resolveJsonModuletrue が設定されています。JSON ファイルの型検証をサポートするため、特段理由がなければ resolveJsonModule を有効化しておくことをお勧めします。また、仮に false を設定しても、Next.js によって強制的に resolveJsonModule が有効化されます。

まとめ

この記事では resolveJsonModulefalse の場合と true の場合で動作を確認しました。Node.js & TypeScript のプロジェクトと Next.js のプロジェクトで動作確認を行いました。

Discussion