🕌

tsconfig.jsonのskipLibCheck

2024/03/25に公開

まとめ

概要
skipLibChecktrue にすると、*.d.ts ファイルに対する型チェックを行わないようになります。メリットは型チェックの実行時間を短縮できます。デメリットは *.d.ts の型チェックが行われないため、*.d.ts の型誤りに気が付かなくなることです。

Next.js
create next-app で作成した Next.js プロジェクトで skipLibChecktrue が設定されています。skipLibCheckfalse の場合、型チェックで大量のエラーが検出されてしまうため、skipLibCheck は必ず有効化しましょう。

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

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

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

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

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

https://github.com/hayato94087/next-tsconfig-skip-lib-check

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

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

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

skipLibCheckfalse の場合

skipLibCheckfalse の場合、*.d.ts ファイルに対する型チェックが行われます。*.d.ts ファイルに誤りがある場合、エラーが出力されます。

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

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

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

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

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

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

$ pnpm install -D typescript ts-node

tsconfig.json を作成します。

$ npx tsc --init

tsconfig.json を作成します。skipLibCheckfalse を設定しています。

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

git を初期化します。

$ git init

.gitignore を作成します。

$ touch .gitignore

git の管理対象外にするファイルを追加します。

.gitignore
node_modules

user.d.ts ファイルを作成します。

$ mkdir -p types/
$ touch types/user.d.ts

user.d.ts ファイルに以下のように記述します。

src/user.d.ts
type  User = {
  name: string;
  age: number;
  email: string;
}

export type { User };

動作可能な簡易コードを作成します。

$ touch index.ts
index.ts
import { User } from "./types/user";

const user: User = {
  name: "John",
  age: 30,
  email: "hoge@gmail.com",
};

console.log("hello world");
console.log(user);

型をチェックします。特に問題ありません。

$ pnpm run typecheck

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

$ pnpm run dev

hello world
{ name: 'John', age: 30, email: 'hoge@gmail.com' }

処理時間を計測します。Check time が型チェックの時間です。skipLibCheckfalse の場合、Check time が 0.59 秒となっています。

$ pnpm run timecheck

Files:                         43
Lines of Library:           37375
Lines of Definitions:           7
Lines of TypeScript:           10
Lines of JavaScript:            0
Lines of JSON:                  0
Lines of Other:                 0
Identifiers:                41639
Symbols:                    32351
Types:                      14994
Instantiations:              3687
Memory used:               76159K
Assignability cache size:    4126
Identity cache size:            0
Subtype cache size:             0
Strict subtype cache size:      4
I/O Read time:              0.01s
Parse time:                 0.21s
ResolveModule time:         0.00s
ResolveLibrary time:        0.02s
Program time:               0.27s
Bind time:                  0.08s
Check time:                 0.59s
printTime time:             0.00s
Emit time:                  0.00s
Total time:                 0.94s

skipLibChecktrue にすると、*.d.ts の型チェックを行わないことで、型チェックの時間を短縮できます。デメリットは *.d.ts の型チェックが行われないため、*.d.ts の記載誤りに気が付かなくなる可能性があることです。ここでは、あえて誤りを追加し、skipLibCheckfalse の時に、誤りを検知できることを確認します。

src/user.d.ts
type  User = {
  name: string;
  age: number;
- email: string;
+ email: string?;
}

export type { User };

型をチェックします。想定通り *.d.ts に混入させたエラーは出ました。

$ pnpm run typecheck

types/user.d.ts:4:10 - error TS17019: '?' at the end of a type is not valid TypeScript syntax. Did you mean to write 'string | undefined'?

4   email: string?;
           ~~~~~~~


Found 1 error in types/user.d.ts:4

誤りを修正します。

src/user.d.ts
type  User = {
  name: string;
  age: number;
- email: string?;
+ email: string;
}

export type { User };

コミットします。

$ git add .
$ git commit -m "feat:skipLibCheckを無効化し処理時間を確認"

skipLibChecktrue の場合

skipLibChecktrue の場合、*.d.ts ファイルに対する型チェックが行われません。*.d.ts に対して型チェックを行わないことで、全体の型チェックの処理時間を短縮できます。

skipLibCheck を有効化します。

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

型をチェックします。特にエラーは出ません。

$ pnpm run typecheck

ローカルの開発環境で動作確認します。問題無く動作します。

$ pnpm run dev

hello world
{ name: 'John', age: 30, email: 'hoge@gmail.com' }

処理時間を計測します。Check time が型チェックの時間です。skipLibChecktrue の場合、Check time が 0.02 秒となっています。

$ pnpm run timecheck

Files:                         43
Lines of Library:           37375
Lines of Definitions:           7
Lines of TypeScript:           10
Lines of JavaScript:            0
Lines of JSON:                  0
Lines of Other:                 0
Identifiers:                41639
Symbols:                    27475
Types:                        101
Instantiations:                 1
Memory used:               54369K
Assignability cache size:       1
Identity cache size:            0
Subtype cache size:             0
Strict subtype cache size:      0
I/O Read time:              0.01s
Parse time:                 0.20s
ResolveModule time:         0.00s
ResolveLibrary time:        0.02s
Program time:               0.26s
Bind time:                  0.08s
Check time:                 0.02s
printTime time:             0.00s
Emit time:                  0.00s
Total time:                 0.36s

skipLibChecktrue にすると、*.d.ts の型チェックを行わないことで、型チェックの時間を短縮できます。デメリットは *.d.ts の型チェックが行われないため、*.d.ts の記載誤りに気が付かなくなる可能性があることです。ここでは、あえて誤りを追加し、skipLibChecktrue の時に、誤りを検知できないことを確認します。

src/user.d.ts
type  User = {
  name: string;
  age: number;
- email: string;
+ email: string?;
}

export type { User };

型をチェックします。想定通り *.d.ts に混入させたエラーは検知できませんでした。

$ pnpm run typecheck

types/user.d.ts:4:10 - error TS17019: '?' at the end of a type is not valid TypeScript syntax. Did you mean to write 'string | undefined'?

4   email: string?;
           ~~~~~~~


Found 1 error in types/user.d.ts:4

誤りを修正します。

src/user.d.ts
type  User = {
  name: string;
  age: number;
- email: string?;
+ email: string;
}

export type { User };

コミットします。

$ git add .
$ git commit -m "feat: skipLibCheckを有効化し処理時間を計測"

比較

まずは処理時間についてです。Check time を比較します。skipLibCheck を有効化した場合、Check time が 0.59 秒から 0.02 秒と大幅に短縮化されています。

Files skipLibCheck: false skipLibCheck: true
Files 43 41
Lines of Library 37375 37375
Lines of Definitions 7 7
Lines of TypeScript 10 10
Lines of JavaScript 0 0
Lines of JSON 0 0
Lines of Other 0 0
Identifiers 41639 41639
Symbols 32351 27475
Types 14994 101
Instantiations 3687 1
Memory used 76159K 54369K
Assignability cache size 4126 1
Identity cache size 0 0
Subtype cache size 0 0
Strict subtype cache size 4 0
I/O Read time 0.01s 0.01s
Parse time 0.21s 0.20s
ResolveModule time 0.00s 0.00s
ResolveLibrary time 0.02s 0.02s
Program time 0.27s 0.26s
Bind time 0.08s 0.08s
Check time 0.59s 0.02s
printTime time 0.00s 0.00s
Emit time 0.00s 0.00s
Total time 0.94s 0.36s

skipLibChecktrue にすると、*.d.ts の型チェックを行わないことで、型チェックの時間は短縮できました。一方で、*.d.ts の型チェックが行われないため、*.d.ts に型誤りがある場合検知できないことも確認できました。

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

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

skipLibCheckfalse の場合

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

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

プロジェクトを作成

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

$ pnpm create next-app@latest next-tsconfig-skip-lib-check --typescript --eslint --import-alias "@/*" --src-dir --use-pnpm --tailwind --app
$ cd next-tsconfig-skip-lib-check
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"]
}

TypeScriptの設定を上書きします。

tsconfig.json
{
  "compilerOptions": {
    "target": "es2017",
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true,
    "checkJs": true,
    "skipLibCheck": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "noEmit": true,
    "esModuleInterop": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "jsx": "preserve",
    "incremental": true,
    "noUncheckedIndexedAccess": true,
    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"]
    },
    "plugins": [
      {
        "name": "next"
      }
    ],
  },
  "include": [
    ".eslintrc.cjs",
    "next-env.d.ts",
    "**/*.ts",
    "**/*.tsx",
    "**/*.cjs",
    "**/*.mjs",
    ".next/types/**/*.ts"
  ],
  "exclude": ["node_modules"]
}
スクリプトを追加

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

package.json
{
  "name": "next-tsconfig-strict",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint",
+   "typecheck": "tsc --noEmit",
+   "timecheck": "tsc --noEmit --extendedDiagnostics"
  },
  "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.37",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "tailwindcss": "^3.3.0",
    "typescript": "^5"
  }
}
動作確認

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

$ pnpm run dev

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

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

skipLibCheck を無効化します。

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

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

$ rm -rf tsconfig.tsbuildinfo

現時点で型のチェックをすると大量にエラーが出ます。よって、Next.js を利用する場合は、skipLibChecktrue にしておくことをお勧めします。

$ pnpm run typecheck

node_modules/.pnpm/next@14.1.4_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/client/components/app-router.d.ts:17:33 - error TS2694: Namespace '"/Users/hayato94087/Private/next-tsconfig-skip-lib-check/node_modules/.pnpm/@types+react@18.0.0/node_modules/@types/react/jsx-runtime"' has no exported member 'JSX'.

17 }): import("react/jsx-runtime").JSX.Element;
                                   ~~~

node_modules/.pnpm/next@14.1.4_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/client/components/error-boundary.d.ts:31:33 - error TS2694: Namespace '"/Users/hayato94087/Private/next-tsconfig-skip-lib-check/node_modules/.pnpm/@types+react@18.0.0/node_modules/@types/react/jsx-runtime"' has no exported member 'JSX'.

31 }): import("react/jsx-runtime").JSX.Element;
                                   ~~~

node_modules/.pnpm/next@14.1.4_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/client/components/layout-router.d.ts:24:33 - error TS2694: Namespace '"/Users/hayato94087/Private/next-tsconfig-skip-lib-check/node_modules/.pnpm/@types+react@18.0.0/node_modules/@types/react/jsx-runtime"' has no exported member 'JSX'.

24 }): import("react/jsx-runtime").JSX.Element;
                                   ~~~

node_modules/.pnpm/next@14.1.4_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/client/components/not-found-boundary.d.ts:8:147 - error TS2694: Namespace '"/Users/hayato94087/Private/next-tsconfig-skip-lib-check/node_modules/.pnpm/@types+react@18.0.0/node_modules/@types/react/jsx-runtime"' has no exported member 'JSX'.

8 export declare function NotFoundBoundary({ notFound, notFoundStyles, asNotFound, children, }: NotFoundBoundaryProps): import("react/jsx-runtime").JSX.Element;
                                                                                                                                                    ~~~

node_modules/.pnpm/next@14.1.4_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/client/components/static-generation-searchparams-bailout-provider.d.ts:6:33 - error TS2694: Namespace '"/Users/hayato94087/Private/next-tsconfig-skip-lib-check/node_modules/.pnpm/@types+react@18.0.0/node_modules/@types/react/jsx-runtime"' has no exported member 'JSX'.

6 }): import("react/jsx-runtime").JSX.Element;
                                  ~~~

node_modules/.pnpm/next@14.1.4_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/pages/_app.d.ts:16:43 - error TS2694: Namespace '"/Users/hayato94087/Private/next-tsconfig-skip-lib-check/node_modules/.pnpm/@types+react@18.0.0/node_modules/@types/react/jsx-runtime"' has no exported member 'JSX'.

16     render(): import("react/jsx-runtime").JSX.Element;
                                             ~~~

node_modules/.pnpm/next@14.1.4_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/pages/_document.d.ts:22:61 - error TS2694: Namespace '"/Users/hayato94087/Private/next-tsconfig-skip-lib-check/node_modules/.pnpm/@types+react@18.0.0/node_modules/@types/react/jsx-runtime"' has no exported member 'JSX'.

22     getPreloadDynamicChunks(): (import("react/jsx-runtime").JSX.Element | null)[];
                                                               ~~~

node_modules/.pnpm/next@14.1.4_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/pages/_document.d.ts:24:70 - error TS2694: Namespace '"/Users/hayato94087/Private/next-tsconfig-skip-lib-check/node_modules/.pnpm/@types+react@18.0.0/node_modules/@types/react/jsx-runtime"' has no exported member 'JSX'.

24     getBeforeInteractiveInlineScripts(): import("react/jsx-runtime").JSX.Element[];
                                                                        ~~~

node_modules/.pnpm/next@14.1.4_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/pages/_document.d.ts:25:74 - error TS2694: Namespace '"/Users/hayato94087/Private/next-tsconfig-skip-lib-check/node_modules/.pnpm/@types+react@18.0.0/node_modules/@types/react/jsx-runtime"' has no exported member 'JSX'.

25     getDynamicChunks(files: DocumentFiles): (import("react/jsx-runtime").JSX.Element | null)[];
                                                                            ~~~

node_modules/.pnpm/next@14.1.4_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/pages/_document.d.ts:26:54 - error TS2694: Namespace '"/Users/hayato94087/Private/next-tsconfig-skip-lib-check/node_modules/.pnpm/@types+react@18.0.0/node_modules/@types/react/jsx-runtime"' has no exported member 'JSX'.

26     getPreNextScripts(): import("react/jsx-runtime").JSX.Element;
                                                        ~~~

node_modules/.pnpm/next@14.1.4_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/pages/_document.d.ts:27:67 - error TS2694: Namespace '"/Users/hayato94087/Private/next-tsconfig-skip-lib-check/node_modules/.pnpm/@types+react@18.0.0/node_modules/@types/react/jsx-runtime"' has no exported member 'JSX'.

27     getScripts(files: DocumentFiles): import("react/jsx-runtime").JSX.Element[];
                                                                     ~~~

node_modules/.pnpm/next@14.1.4_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/pages/_document.d.ts:28:55 - error TS2694: Namespace '"/Users/hayato94087/Private/next-tsconfig-skip-lib-check/node_modules/.pnpm/@types+react@18.0.0/node_modules/@types/react/jsx-runtime"' has no exported member 'JSX'.

28     getPolyfillScripts(): import("react/jsx-runtime").JSX.Element[];
                                                         ~~~

node_modules/.pnpm/next@14.1.4_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/pages/_document.d.ts:30:43 - error TS2694: Namespace '"/Users/hayato94087/Private/next-tsconfig-skip-lib-check/node_modules/.pnpm/@types+react@18.0.0/node_modules/@types/react/jsx-runtime"' has no exported member 'JSX'.

30     render(): import("react/jsx-runtime").JSX.Element;
                                             ~~~

node_modules/.pnpm/next@14.1.4_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/pages/_document.d.ts:35:74 - error TS2694: Namespace '"/Users/hayato94087/Private/next-tsconfig-skip-lib-check/node_modules/.pnpm/@types+react@18.0.0/node_modules/@types/react/jsx-runtime"' has no exported member 'JSX'.

35     getDynamicChunks(files: DocumentFiles): (import("react/jsx-runtime").JSX.Element | null)[];
                                                                            ~~~

node_modules/.pnpm/next@14.1.4_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/pages/_document.d.ts:36:54 - error TS2694: Namespace '"/Users/hayato94087/Private/next-tsconfig-skip-lib-check/node_modules/.pnpm/@types+react@18.0.0/node_modules/@types/react/jsx-runtime"' has no exported member 'JSX'.

36     getPreNextScripts(): import("react/jsx-runtime").JSX.Element;
                                                        ~~~

node_modules/.pnpm/next@14.1.4_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/pages/_document.d.ts:37:67 - error TS2694: Namespace '"/Users/hayato94087/Private/next-tsconfig-skip-lib-check/node_modules/.pnpm/@types+react@18.0.0/node_modules/@types/react/jsx-runtime"' has no exported member 'JSX'.

37     getScripts(files: DocumentFiles): import("react/jsx-runtime").JSX.Element[];
                                                                     ~~~

node_modules/.pnpm/next@14.1.4_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/pages/_document.d.ts:38:55 - error TS2694: Namespace '"/Users/hayato94087/Private/next-tsconfig-skip-lib-check/node_modules/.pnpm/@types+react@18.0.0/node_modules/@types/react/jsx-runtime"' has no exported member 'JSX'.

38     getPolyfillScripts(): import("react/jsx-runtime").JSX.Element[];
                                                         ~~~

node_modules/.pnpm/next@14.1.4_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/pages/_document.d.ts:40:43 - error TS2694: Namespace '"/Users/hayato94087/Private/next-tsconfig-skip-lib-check/node_modules/.pnpm/@types+react@18.0.0/node_modules/@types/react/jsx-runtime"' has no exported member 'JSX'.

40     render(): import("react/jsx-runtime").JSX.Element | null;
                                             ~~~

node_modules/.pnpm/next@14.1.4_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/pages/_document.d.ts:42:151 - error TS2694: Namespace '"/Users/hayato94087/Private/next-tsconfig-skip-lib-check/node_modules/.pnpm/@types+react@18.0.0/node_modules/@types/react/jsx-runtime"' has no exported member 'JSX'.

42 export declare function Html(props: React.DetailedHTMLProps<React.HtmlHTMLAttributes<HTMLHtmlElement>, HTMLHtmlElement>): import("react/jsx-runtime").JSX.Element;
                                                                                                                                                         ~~~

node_modules/.pnpm/next@14.1.4_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/pages/_document.d.ts:43:61 - error TS2694: Namespace '"/Users/hayato94087/Private/next-tsconfig-skip-lib-check/node_modules/.pnpm/@types+react@18.0.0/node_modules/@types/react/jsx-runtime"' has no exported member 'JSX'.

43 export declare function Main(): import("react/jsx-runtime").JSX.Element;
                                                               ~~~

node_modules/.pnpm/next@14.1.4_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/pages/_document.d.ts:54:43 - error TS2694: Namespace '"/Users/hayato94087/Private/next-tsconfig-skip-lib-check/node_modules/.pnpm/@types+react@18.0.0/node_modules/@types/react/jsx-runtime"' has no exported member 'JSX'.

54     render(): import("react/jsx-runtime").JSX.Element;
                                             ~~~

node_modules/.pnpm/next@14.1.4_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/pages/_error.d.ts:16:43 - error TS2694: Namespace '"/Users/hayato94087/Private/next-tsconfig-skip-lib-check/node_modules/.pnpm/@types+react@18.0.0/node_modules/@types/react/jsx-runtime"' has no exported member 'JSX'.

16     render(): import("react/jsx-runtime").JSX.Element;
                                             ~~~

node_modules/.pnpm/next@14.1.4_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/shared/lib/head.d.ts:9:33 - error TS2694: Namespace '"/Users/hayato94087/Private/next-tsconfig-skip-lib-check/node_modules/.pnpm/@types+react@18.0.0/node_modules/@types/react/jsx-runtime"' has no exported member 'JSX'.

9 }): import("react/jsx-runtime").JSX.Element;
                                  ~~~


Found 23 errors in 9 files.

Errors  Files
     1  node_modules/.pnpm/next@14.1.4_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/client/components/app-router.d.ts:17
     1  node_modules/.pnpm/next@14.1.4_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/client/components/error-boundary.d.ts:31
     1  node_modules/.pnpm/next@14.1.4_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/client/components/layout-router.d.ts:24
     1  node_modules/.pnpm/next@14.1.4_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/client/components/not-found-boundary.d.ts:8
     1  node_modules/.pnpm/next@14.1.4_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/client/components/static-generation-searchparams-bailout-provider.d.ts:6
     1  node_modules/.pnpm/next@14.1.4_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/pages/_app.d.ts:16
    15  node_modules/.pnpm/next@14.1.4_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/pages/_document.d.ts:22
     1  node_modules/.pnpm/next@14.1.4_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/pages/_error.d.ts:16
     1  node_modules/.pnpm/next@14.1.4_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/shared/lib/head.d.ts:9

ビルドします。上記と同様のエラーが検出されます。

$ pnpm run build

   ▲ Next.js 14.1.4

   Creating an optimized production build ...
 ✓ Compiled successfully
   Linting and checking validity of types  ..Failed to compile.

   Linting and checking validity of types  ..../node_modules/.pnpm/next@14.1.4_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/client/components/app-router.d.ts:17:33
Type error: Namespace '"/Users/hayato94087/Private/next-tsconfig-skip-lib-check/node_modules/.pnpm/@types+react@18.0.0/node_modules/@types/react/jsx-runtime"' has no exported member 'JSX'.

  15 | export default function AppRouter(props: AppRouterProps & {
  16 |     globalErrorComponent: ErrorComponent;
> 17 | }): import("react/jsx-runtime").JSX.Element;
     |                                 ^
  18 | export {};
  19 |

コミットします。

$ git add .
$ git commit -m "feat:skipLibCheckを無効化し動作を確認"

skipLibChecktrue の場合

skipLibCheck を無効化します。

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

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

$ rm -rf tsconfig.tsbuildinfo

現時点で型のチェックするとエラーが出なくなります。

$ pnpm run typecheck

ビルドします。エラーは検出されません。

$ pnpm run build

   ▲ Next.js 14.1.4

   Creating an optimized production build ...
 ✓ Compiled successfully
 ✓ Linting and checking validity of types    
 ✓ Collecting page data    
 ✓ 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

コミットします。

$ git add .
$ git commit -m "feat:skipLibCheckを有効化し動作を確認"

Next.jsの設定考察

create next-app で作成した Next.js プロジェクトで skipLibChecktrue が設定されています。skipLibCheckfalse の場合、型チェックで大量のエラーが検出されてしまうため、skipLibCheck は必ず有効化しましょう。

さいごに

skipLibCheck は、*.d.ts の型チェックを行わないことで、型チェックの時間を短縮できます。一方で、*.d.ts の型チェックが行われないため、*.d.ts に型誤りがある場合検知できないことも確認できました。Node.js & TypeScript のプロジェクトで skipLibCheck を有効化することで、型チェックの時間を短縮できることを確認しました。また、Next.js のプロジェクトで skipLibCheck を有効化しないとビルドエラーが発生することを確認しました。よって、Next.js では skipLibCheck は有効化することが必須です。

Discussion