TypeScriptよくあるエラーTOP30と解決法〜TS2322・TS2345・TS7006他完全攻略【2026年版】〜

「TS2322が消えない」「TS7006でビルドが落ちる」「TS2532で延々と ! を付けて回ってる」――TypeScriptを書いていれば誰もが一度は通る道です。本記事は TypeScript 5.x を前提に、現場で本当に頻出するエラー 30+パターン を「エラーコード→再現コード(Before)→修正コード(After)→なぜ起きるか」の4点セットで一気に潰していく実践リファレンスです。

すべてのコードは strict: true / target: ES2022 / moduleResolution: bundler 環境で動作検証済み。コピペでそのまま動かして手で覚えてください。

  1. 1. TS2322 Type ‘X’ is not assignable to type ‘Y’
    1. 1-1. プリミティブの不一致
    2. 1-2. ユニオン型に対するリテラル割り当て
    3. 1-3. 戻り値型の不一致(関数)
  2. 2. TS2345 Argument of type ‘X’ is not assignable to parameter of type ‘Y’
    1. 2-1. 引数の型が緩すぎる
    2. 2-2. オブジェクト引数の余剰プロパティ
  3. 3. TS7006 Parameter implicitly has an ‘any’ type
    1. 3-1. コールバックの引数型忘れ
    2. 3-2. イベントハンドラの引数型忘れ(React)
  4. 4. TS2339 Property ‘X’ does not exist on type ‘Y’
    1. 4-1. ユニオン型での絞り込み不足
    2. 4-2. window 拡張のグローバル宣言
  5. 5. TS2304 Cannot find name ‘X’
    1. 5-1. importの書き忘れ
    2. 5-2. DOM API が見つからない
  6. 6. TS2532 Object is possibly ‘undefined’
    1. 6-1. 配列要素アクセス
    2. 6-2. オプショナルプロパティ
  7. 7. TS2531 Object is possibly ‘null’
    1. 7-1. getElementById の null チェック
    2. 7-2. useRef の current が null
  8. 8. TS18046 ‘X’ is of type ‘unknown’
    1. 8-1. catch句のerror
    2. 8-2. JSON.parse の結果
  9. 9. TS2740 Type ‘X’ is missing the following properties from type ‘Y’
    1. 9-1. 必須プロパティの抜け
    2. 9-2. デフォルト値のためのPartial
  10. 10. TS2741 Property ‘X’ is missing in type ‘Y’ but required in type ‘Z’
    1. 10-1. 1プロパティ抜け
  11. 11. TS2769 No overload matches this call
    1. 11-1. setTimeout の引数ミス
    2. 11-2. addEventListener 型ミス
  12. 12. TS2367 This comparison appears to be unintentional
    1. 12-1. 型が重ならない比較
  13. 13. TS2454 Variable ‘X’ is used before being assigned
    1. 13-1. 条件分岐で漏れた代入
  14. 14. TS2705 An async function or method … must have a valid awaitable return type
    1. 14-1. async関数の戻り値型ミス
  15. 15. TS18047 ‘X’ is possibly ‘null’
    1. 15-1. localStorage の値
  16. 16. TS2554 Expected N arguments, but got M
    1. 16-1. 引数不足
    2. 16-2. React onClick で過剰引数
  17. 17. TS2349 This expression is not callable
    1. 17-1. ユニオン型に関数を持つもの
  18. 18. TS2353 Object literal may only specify known properties
    1. 18-1. propsの余計なキー
  19. 19. TS2786 ‘X’ cannot be used as a JSX component
    1. 19-1. 配列を直接返している
    2. 19-2. @types/reactのバージョン不整合
  20. 20. TS6133 ‘X’ is declared but its value is never read
    1. 20-1. 未使用の引数
    2. 20-2. 未使用のimport
  21. 21. TS17004 Cannot use JSX unless the ‘–jsx’ flag is provided
    1. 21-1. tsconfig.json の jsx 未設定
  22. 22. TS2305 Module ‘X’ has no exported member ‘Y’
    1. 22-1. デフォルトエクスポートを名前付きで取ろうとした
  23. 23. TS2307 Cannot find module ‘X’ or its corresponding type declarations
    1. 23-1. 型定義パッケージ未インストール
    2. 23-2. パスエイリアスの未設定
  24. 24. TS2742 The inferred type of ‘X’ cannot be named without a reference
    1. 24-1. 明示的な型注釈で回避
  25. 25. TS1259 Module ‘X’ can only be default-imported using the ‘esModuleInterop’ flag
    1. 25-1. esModuleInterop の有効化
  26. 26. TS6133 unused variable(再掲・実務パターン)
    1. 26-1. 分割代入で先頭だけ欲しい
  27. 27. TS2802 Type ‘X’ can only be iterated through when using the ‘–downlevelIteration’ flag
    1. 27-1. tsconfigを近代化する
    2. 27-2. どうしてもES5が必要な場合
  28. 28. TS5023 Unknown compiler option ‘X’
    1. 28-1. オプション名タイポ
    2. 28-2. TypeScriptのバージョン不足
  29. 29. TS1192 Module ‘X’ has no default export
    1. 29-1. 名前付きimportに変える
  30. 30. TS2304 Cannot find name(環境変数)
    1. 30-1. Node.jsの process
    2. 30-2. Viteの import.meta.env を型安全に
  31. 31. TS2589 Type instantiation is excessively deep and possibly infinite
    1. 31-1. 再帰の打ち切り
  32. 32. tsconfig関連エラー総まとめ
    1. 32-1. Web(Vite/React)向け推奨設定
    2. 32-2. Node(サーバー)向け推奨設定
  33. 33. moduleResolution: bundler 移行時のエラー対処
    1. 33-1. 拡張子付きimport要求エラー
    2. 33-2. .ts拡張子つきimport(bundler 限定)
  34. 34. 補助: ESLint/TypeScript Eslint 連携エラー
    1. 34-1. @typescript-eslint パーサ未設定
  35. エラーをそもそも減らすための7つの設計指針
  36. satisfies の実用パターン
  37. 1日でエラー耐性を底上げする学習ルート
  38. よくある質問(FAQ)
    1. Q1. anyを使えばエラーは消えますが、本当にダメですか?
    2. Q2. 非nullアサーション(!)はどこまで許容できますか?
    3. Q3. エラーが多すぎて手をつけられない既存プロジェクト、どう移行しますか?
    4. Q4. TS2589 が出たら型を諦めるしかありませんか?
    5. Q5. verbatimModuleSyntax は有効にすべき?
  39. まとめ

1. TS2322 Type ‘X’ is not assignable to type ‘Y’

もっともよく見るエラー。代入先の型と代入元の型が一致しない、または広すぎる/狭すぎる時に発生します。

1-1. プリミティブの不一致

Before:

// ❌ TS2322: Type 'string' is not assignable to type 'number'.
const age: number = "30";

After:

// ✅ 型を合わせるか、変換する
const age: number = 30;
// 文字列から数値にしたい場合
const ageFromInput: number = Number("30");

1-2. ユニオン型に対するリテラル割り当て

Before:

type Status = "idle" | "loading" | "success" | "error";
// ❌ TS2322: Type 'string' is not assignable to type 'Status'.
let s: Status = "loding"; // タイポ

After:

type Status = "idle" | "loading" | "success" | "error";
// ✅ const assertion で誤入力を即時検知
const s = "loading" as const satisfies Status;

1-3. 戻り値型の不一致(関数)

Before:

// ❌ TS2322: Type 'string' is not assignable to type 'number'.
const getId = (): number => {
  return "u-001";
};

After:

// ✅ 戻り値型を実態に合わせる(またはロジック側を直す)
const getId = (): string => {
  return "u-001";
};

2. TS2345 Argument of type ‘X’ is not assignable to parameter of type ‘Y’

関数呼び出しの引数で型が合わない時に出ます。TS2322の「引数版」と覚えてOK。

2-1. 引数の型が緩すぎる

Before:

function greet(name: string) {
  return `Hello, ${name.toUpperCase()}`;
}
// ❌ TS2345: Argument of type 'number' is not assignable to parameter of type 'string'.
greet(123);

After:

function greet(name: string) {
  return `Hello, ${name.toUpperCase()}`;
}
// ✅ 文字列化して渡す
greet(String(123));

2-2. オブジェクト引数の余剰プロパティ

Before:

type User = { id: string; name: string };
function save(u: User) { /* ... */ }
// ❌ TS2345 + TS2353: 'role' は User に存在しない
save({ id: "1", name: "yui", role: "admin" });

After:

type User = { id: string; name: string; role?: "admin" | "user" };
function save(u: User) { /* ... */ }
// ✅ User側にroleを足すか、別の型に分離する
save({ id: "1", name: "yui", role: "admin" });

3. TS7006 Parameter implicitly has an ‘any’ type

noImplicitAny(strict 配下で自動有効)で、関数パラメータの型注釈忘れに出るエラーです。

3-1. コールバックの引数型忘れ

Before:

// ❌ TS7006: Parameter 'item' implicitly has an 'any' type.
const formatItems = (items) => items.map((item) => item.name);

After:

type Item = { id: string; name: string };
// ✅ 明示的に型を付ける
const formatItems = (items: Item[]): string[] =>
  items.map((item) => item.name);

3-2. イベントハンドラの引数型忘れ(React)

Before:

// ❌ TS7006: Parameter 'e' implicitly has an 'any' type.
const onChange = (e) => console.log(e.target.value);

After:

import type { ChangeEvent } from "react";
// ✅ React.ChangeEventで型を明示
const onChange = (e: ChangeEvent<HTMLInputElement>) =>
  console.log(e.target.value);

4. TS2339 Property ‘X’ does not exist on type ‘Y’

型にないプロパティへアクセスした時のエラー。実は型定義が古い、または型を絞り込めていないことが原因のことが多いです。

4-1. ユニオン型での絞り込み不足

Before:

type ApiResult =
  | { ok: true; data: string }
  | { ok: false; error: string };

function show(res: ApiResult) {
  // ❌ TS2339: Property 'data' does not exist on type ...
  console.log(res.data);
}

After:

type ApiResult =
  | { ok: true; data: string }
  | { ok: false; error: string };

function show(res: ApiResult) {
  // ✅ 判別可能ユニオンで絞り込んでからアクセス
  if (res.ok) {
    console.log(res.data);
  } else {
    console.error(res.error);
  }
}

4-2. window 拡張のグローバル宣言

Before:

// ❌ TS2339: Property 'gtag' does not exist on type 'Window & typeof globalThis'.
window.gtag("event", "click");

After:

// global.d.ts などで宣言マージ
declare global {
  interface Window {
    gtag: (cmd: string, ...args: unknown[]) => void;
  }
}
export {};

// ✅ 以降は型エラーなくアクセス可能
window.gtag("event", "click");

5. TS2304 Cannot find name ‘X’

その識別子が見つからない(=スコープに存在しない)エラー。importミスかtsconfigのlib不足が大半です。

5-1. importの書き忘れ

Before:

// ❌ TS2304: Cannot find name 'useState'.
const [count, setCount] = useState(0);

After:

// ✅ react から useState をインポート
import { useState } from "react";
const [count, setCount] = useState(0);

5-2. DOM API が見つからない

Before:

// ❌ TS2304: Cannot find name 'document'.
const root = document.getElementById("root");

After(tsconfig.json):

{
  "compilerOptions": {
    "target": "ES2022",
    "lib": ["ES2022", "DOM", "DOM.Iterable"]
  }
}

6. TS2532 Object is possibly ‘undefined’

strictNullChecks下で、undefinedになりうる値へアクセスした時のエラー。! で潰すのは最終手段で、原則は絞り込みで対処します。

6-1. 配列要素アクセス

Before:

const users = ["alice", "bob"];
// noUncheckedIndexedAccess: true の場合
// ❌ TS2532: Object is possibly 'undefined'.
console.log(users[2].toUpperCase());

After:

const users = ["alice", "bob"];
const first = users[0];
// ✅ オプショナルチェイニング+デフォルト値
console.log(first?.toUpperCase() ?? "no user");

6-2. オプショナルプロパティ

Before:

type User = { name: string; address?: { city: string } };
function showCity(u: User) {
  // ❌ TS2532: Object is possibly 'undefined'.
  return u.address.city;
}

After:

type User = { name: string; address?: { city: string } };
function showCity(u: User) {
  // ✅ Optional Chaining で安全にアクセス
  return u.address?.city ?? "(unknown)";
}

7. TS2531 Object is possibly ‘null’

TS2532のnull版。DOM API で頻発します。

7-1. getElementById の null チェック

Before:

// ❌ TS2531: Object is possibly 'null'.
const el = document.getElementById("app");
el.innerHTML = "hello";

After:

// ✅ 存在チェック → 早期return
const el = document.getElementById("app");
if (!el) throw new Error("#app not found");
el.innerHTML = "hello";

7-2. useRef の current が null

Before:

import { useRef, useEffect } from "react";
function Focus() {
  const ref = useRef<HTMLInputElement>(null);
  useEffect(() => {
    // ❌ TS2531: Object is possibly 'null'.
    ref.current.focus();
  }, []);
  return <input ref={ref} />;
}

After:

import { useRef, useEffect } from "react";
function Focus() {
  const ref = useRef<HTMLInputElement>(null);
  useEffect(() => {
    // ✅ null チェック後にメソッド呼び出し
    ref.current?.focus();
  }, []);
  return <input ref={ref} />;
}

8. TS18046 ‘X’ is of type ‘unknown’

catch句のerror、JSON.parseの戻り値、APIレスポンスなど、安全に型が決まらないものは unknown になります。直接プロパティアクセスできません。

8-1. catch句のerror

Before:

try {
  await fetch("/api/x");
} catch (e) {
  // ❌ TS18046: 'e' is of type 'unknown'.
  console.error(e.message);
}

After:

try {
  await fetch("/api/x");
} catch (e) {
  // ✅ instanceof で絞り込む
  if (e instanceof Error) {
    console.error(e.message);
  } else {
    console.error("unknown error", e);
  }
}

8-2. JSON.parse の結果

Before:

const raw = '{"id":1,"name":"alice"}';
const data: unknown = JSON.parse(raw);
// ❌ TS18046: 'data' is of type 'unknown'.
console.log(data.name);

After:

import { z } from "zod";

const UserSchema = z.object({ id: z.number(), name: z.string() });
const raw = '{"id":1,"name":"alice"}';
// ✅ Zodで実行時バリデーション+型導出
const data = UserSchema.parse(JSON.parse(raw));
console.log(data.name); // string と確定

9. TS2740 Type ‘X’ is missing the following properties from type ‘Y’

必須プロパティが大量に足りない時のエラー。空オブジェクトに対する初期化でよく出ます。

9-1. 必須プロパティの抜け

Before:

type Config = { host: string; port: number; secure: boolean };
// ❌ TS2740: Type '{}' is missing the following properties...
const c: Config = {};

After:

type Config = { host: string; port: number; secure: boolean };
// ✅ すべての必須プロパティを与える
const c: Config = { host: "localhost", port: 5432, secure: false };

9-2. デフォルト値のためのPartial

Before:

type Config = { host: string; port: number; secure: boolean };
// ❌ 初期値だけ作って後でマージしたいのに必須が辛い
const defaults: Config = { host: "localhost" }; // TS2740

After:

type Config = { host: string; port: number; secure: boolean };
// ✅ Partial<T> で「全プロパティ任意」化
const defaults: Partial<Config> = { host: "localhost" };
const merged: Config = { port: 5432, secure: false, ...defaults };

10. TS2741 Property ‘X’ is missing in type ‘Y’ but required in type ‘Z’

TS2740のピンポイント版。1個だけ足りない時に出ます。

10-1. 1プロパティ抜け

Before:

type Props = { title: string; description: string };
// ❌ TS2741: Property 'description' is missing
const p: Props = { title: "Hello" };

After:

type Props = { title: string; description: string };
// ✅ 足すか、 description?: string に変更する
const p: Props = { title: "Hello", description: "" };

11. TS2769 No overload matches this call

オーバーロードのある関数(setTimeout、addEventListener、JSXコンポーネント等)で、引数の組み合わせがどのシグネチャにも合致しない時のエラー。

11-1. setTimeout の引数ミス

Before:

// ❌ TS2769: No overload matches this call.
setTimeout("console.log('hi')", 1000); // 文字列はNG

After:

// ✅ 関数(コールバック)を渡す
setTimeout(() => console.log("hi"), 1000);

11-2. addEventListener 型ミス

Before:

const btn = document.querySelector("button")!;
// ❌ TS2769: No overload matches this call.
btn.addEventListener("clicked", (e) => {}); // イベント名タイポ

After:

const btn = document.querySelector("button")!;
// ✅ 正しいイベント名で文字列リテラル型と一致させる
btn.addEventListener("click", (e: MouseEvent) => {
  console.log(e.clientX);
});

12. TS2367 This comparison appears to be unintentional

比較の両辺の型に重なりがないと出る親切エラー。バグ予備軍を検出してくれます。

12-1. 型が重ならない比較

Before:

type Role = "admin" | "user";
function isOwner(role: Role) {
  // ❌ TS2367: This condition will always return 'false'.
  return role === "owner";
}

After:

type Role = "admin" | "user" | "owner";
function isOwner(role: Role) {
  // ✅ Role に "owner" を加える、または比較自体を見直す
  return role === "owner";
}

13. TS2454 Variable ‘X’ is used before being assigned

宣言だけして代入していない変数を使った時のエラー。条件分岐の中で代入する書き方で発生しがちです。

13-1. 条件分岐で漏れた代入

Before:

let result: string;
const flag = Math.random() > 0.5;
if (flag) {
  result = "A";
}
// ❌ TS2454: Variable 'result' is used before being assigned.
console.log(result);

After:

// ✅ 初期値を必ず持たせる、または else を埋める
let result: string = "default";
const flag = Math.random() > 0.5;
if (flag) result = "A";
else result = "B";
console.log(result);

14. TS2705 An async function or method … must have a valid awaitable return type

async関数の戻り値型が Promise<T> 形式になっていない時のエラー(主に古いlibや誤った型指定)。

14-1. async関数の戻り値型ミス

Before:

// ❌ TS2705 / TS1064 系: async は Promise を返す必要がある
async function getName(): string {
  return "alice";
}

After:

// ✅ Promise<T> を返す
async function getName(): Promise<string> {
  return "alice";
}

15. TS18047 ‘X’ is possibly ‘null’

TS2531系のうち、メソッド呼び出しではなく値そのものへのアクセスで出るバージョン。

15-1. localStorage の値

Before:

const token = localStorage.getItem("token");
// ❌ TS18047: 'token' is possibly 'null'.
const length: number = token.length;

After:

const token = localStorage.getItem("token");
// ✅ null合体で空文字フォールバック
const length: number = (token ?? "").length;

16. TS2554 Expected N arguments, but got M

関数の引数の数が違うエラー。多すぎる/少なすぎる両方で出ます。

16-1. 引数不足

Before:

function divide(a: number, b: number) {
  return a / b;
}
// ❌ TS2554: Expected 2 arguments, but got 1.
divide(10);

After:

// ✅ デフォルト値か、オプショナル引数で柔軟に
function divide(a: number, b: number = 1) {
  return a / b;
}
divide(10);

16-2. React onClick で過剰引数

Before:

const handleClick = (id: string) => console.log(id);
// ❌ TS2554: Expected 1 arguments, but got 0.
return <button onClick={handleClick}>Click</button>;

After:

const handleClick = (id: string) => console.log(id);
// ✅ アロー関数でラップし、必要な引数を束ねる
return <button onClick={() => handleClick("u-001")}>Click</button>;

17. TS2349 This expression is not callable

関数として呼び出せない値を呼んだ時。配列メソッドのチェーンミスでよく出ます。

17-1. ユニオン型に関数を持つもの

Before:

type Handler = ((s: string) => void) | string;
function run(h: Handler) {
  // ❌ TS2349: This expression is not callable.
  h("hi");
}

After:

type Handler = ((s: string) => void) | string;
function run(h: Handler) {
  // ✅ typeof で絞り込む
  if (typeof h === "function") {
    h("hi");
  } else {
    console.log(h);
  }
}

18. TS2353 Object literal may only specify known properties

余剰プロパティチェック。オブジェクトリテラルにだけ厳格に適用されます。

18-1. propsの余計なキー

Before:

type ButtonProps = { label: string; onClick: () => void };
// ❌ TS2353: 'color' does not exist in type 'ButtonProps'.
const props: ButtonProps = { label: "OK", onClick: () => {}, color: "red" };

After:

type ButtonProps = { label: string; onClick: () => void; color?: string };
// ✅ 型を拡張するか、不要なプロパティを削除
const props: ButtonProps = { label: "OK", onClick: () => {}, color: "red" };

19. TS2786 ‘X’ cannot be used as a JSX component

Reactコンポーネントが返している値が ReactNode と互換性がない時のエラー。version不一致でも出ます。

19-1. 配列を直接返している

Before:

// ❌ TS2786: Its return type 'string[]' is not a valid JSX element.
const List = (): string[] => ["a", "b", "c"];

After:

// ✅ JSX で包む、または Fragment を使う
const List = () => (
  <>
    {["a", "b", "c"].map((x) => (
      <span key={x}>{x}</span>
    ))}
  </>
);

19-2. @types/reactのバージョン不整合

Before:

# ❌ TS2786 が消えない時、@types/react と react のメジャー版がズレている
npm ls react @types/react
# react@19.0.0 / @types/react@18.x のような不一致

After:

# ✅ メジャー版を揃える
npm i -E @types/react@19 @types/react-dom@19

20. TS6133 ‘X’ is declared but its value is never read

未使用変数の警告。noUnusedLocals / noUnusedParameters で出ます。

20-1. 未使用の引数

Before:

// ❌ TS6133: 'err' is declared but its value is never read.
function safeParse(json: string, err: unknown) {
  return JSON.parse(json);
}

After:

// ✅ アンダースコア接頭辞で「意図的に未使用」を表明
function safeParse(json: string, _err: unknown) {
  return JSON.parse(json);
}

20-2. 未使用のimport

Before:

// ❌ TS6133: 'useMemo' is declared but its value is never read.
import { useState, useMemo } from "react";
const [v, setV] = useState(0);

After:

// ✅ 使わないものは消す(自動修正はESLintのno-unused-importsが便利)
import { useState } from "react";
const [v, setV] = useState(0);

21. TS17004 Cannot use JSX unless the ‘–jsx’ flag is provided

tsconfigの設定漏れ。Reactを書いていて初期段階に出ることが多い。

21-1. tsconfig.json の jsx 未設定

Before:

// ❌ TS17004: jsx の指定がない
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "ESNext"
  }
}

After:

// ✅ React 17+は "react-jsx"(自動JSXランタイム)
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "ESNext",
    "jsx": "react-jsx",
    "esModuleInterop": true
  }
}

22. TS2305 Module ‘X’ has no exported member ‘Y’

名前付きエクスポートが存在しないモジュールから名前付きインポートしているエラー。

22-1. デフォルトエクスポートを名前付きで取ろうとした

Before:

// ❌ TS2305: Module '"react"' has no exported member 'React'.
import { React } from "react";

After:

// ✅ ReactはデフォルトExport
import React from "react";
// もしくは個別フックだけ取り出す
import { useState, useEffect } from "react";

23. TS2307 Cannot find module ‘X’ or its corresponding type declarations

パスかinstallミス。@types/xxx 未インストールが定番。

23-1. 型定義パッケージ未インストール

Before:

// ❌ TS2307: Cannot find module 'lodash' or its corresponding type declarations.
import _ from "lodash";

After:

# ✅ 型定義をdevDependencyで入れる
npm i -D @types/lodash

23-2. パスエイリアスの未設定

Before:

// ❌ TS2307: Cannot find module '@/lib/api' or its corresponding type declarations.
import { fetchUser } from "@/lib/api";

After(tsconfig.json):

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"]
    }
  }
}

24. TS2742 The inferred type of ‘X’ cannot be named without a reference

モノレポやライブラリ作者で頻発する厄介エラー。ライブラリの内部型に依存した推論が型宣言に出せない状態。

24-1. 明示的な型注釈で回避

Before:

// ❌ TS2742: The inferred type of 'createStore' cannot be named ...
import { create } from "zustand";

export const useStore = create((set) => ({
  count: 0,
  inc: () => set((s: { count: number }) => ({ count: s.count + 1 })),
}));

After:

// ✅ 戻り型を明示し、依存型をimport
import { create, type StoreApi } from "zustand";

type CounterStore = { count: number; inc: () => void };

export const useStore = create<CounterStore>((set) => ({
  count: 0,
  inc: () => set((s) => ({ count: s.count + 1 })),
}));

25. TS1259 Module ‘X’ can only be default-imported using the ‘esModuleInterop’ flag

CJSをESM風に書きたい時の代表エラー。tsconfigで一発解決します。

25-1. esModuleInterop の有効化

Before:

// ❌ TS1259発生する設定
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "ESNext"
  }
}

After:

// ✅ esModuleInterop と allowSyntheticDefaultImports を有効に
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "ESNext",
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true
  }
}

26. TS6133 unused variable(再掲・実務パターン)

分割代入での未使用変数は別の書き方で潰せます。

26-1. 分割代入で先頭だけ欲しい

Before:

// ❌ TS6133: 'b' is declared but its value is never read.
const [a, b] = [1, 2];
console.log(a);

After:

// ✅ アンダースコアで未使用宣言
const [a, _b] = [1, 2];
console.log(a);
// もしくは添字アクセス
const arr = [1, 2];
console.log(arr[0]);

27. TS2802 Type ‘X’ can only be iterated through when using the ‘–downlevelIteration’ flag

古いtarget(ES5など)で Map/Set/Iteratorをfor-ofで回した時のエラー。

27-1. tsconfigを近代化する

Before:

// ❌ TS2802 を誘発する古い設定
{
  "compilerOptions": {
    "target": "ES5",
    "module": "CommonJS"
  }
}

After:

// ✅ ES2022 / downlevelIteration は実質不要に
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "ESNext",
    "lib": ["ES2022", "DOM"]
  }
}

27-2. どうしてもES5が必要な場合

Before:

const map = new Map<string, number>([["a", 1]]);
// ❌ TS2802 (target: ES5)
for (const [k, v] of map) console.log(k, v);

After:

// ✅ downlevelIteration を有効に
{
  "compilerOptions": {
    "target": "ES5",
    "downlevelIteration": true
  }
}

28. TS5023 Unknown compiler option ‘X’

tsconfigのキーが古い/誤字/typescriptのバージョン非対応。意外と多い。

28-1. オプション名タイポ

Before:

{
  "compilerOptions": {
    // ❌ TS5023: Unknown compiler option 'strictNull'.
    "strictNull": true
  }
}

After:

{
  "compilerOptions": {
    // ✅ 正しいのは strictNullChecks(または strict)
    "strict": true
  }
}

28-2. TypeScriptのバージョン不足

Before:

# ❌ TS5023: Unknown compiler option 'verbatimModuleSyntax'.(TS4.4 で4.7+専用機能)
tsc --version
# Version 4.4.x

After:

# ✅ TypeScriptを5.x系へ更新
npm i -D -E typescript@latest
tsc --version
# Version 5.6.x

29. TS1192 Module ‘X’ has no default export

TS2305と双子のようなエラー。デフォルトエクスポートがないモジュールにdefaultで入った時に出る。

29-1. 名前付きimportに変える

Before:

// ❌ TS1192: Module '"./utils"' has no default export.
import utils from "./utils";

// utils.ts:
// export function add(a: number, b: number) { return a + b; }

After:

// ✅ 名前付きで取り出す
import { add } from "./utils";
add(1, 2);

// もしくは namespace import
import * as utils from "./utils";
utils.add(1, 2);

30. TS2304 Cannot find name(環境変数)

process.env や Vite の import.meta.env で出る典型エラー。

30-1. Node.jsの process

Before:

// ❌ TS2304: Cannot find name 'process'.
const port = process.env.PORT;

After:

# ✅ @types/node を入れる
npm i -D @types/node
// tsconfig.json
{
  "compilerOptions": {
    "types": ["node"]
  }
}

30-2. Viteの import.meta.env を型安全に

Before:

// ❌ TS2339: Property 'VITE_API_URL' does not exist on type 'ImportMetaEnv'.
const url = import.meta.env.VITE_API_URL;

After(src/env.d.ts):

/// <reference types="vite/client" />

interface ImportMetaEnv {
  readonly VITE_API_URL: string;
  readonly VITE_GA_ID: string;
}

interface ImportMeta {
  readonly env: ImportMetaEnv;
}
// ✅ 補完が効くようになる
const url: string = import.meta.env.VITE_API_URL;

31. TS2589 Type instantiation is excessively deep and possibly infinite

再帰的なConditional Types や Mapped Types で爆発した時のエラー。深い型変換のライブラリで頻発します。

31-1. 再帰の打ち切り

Before:

// ❌ TS2589: 再帰深度制限に到達
type Repeat<S extends string, N extends number, Acc extends string = ""> =
  Acc["length"] extends N ? Acc : Repeat<S, N, `${Acc}${S}`>;

type X = Repeat<"a", 1000>;

After:

// ✅ 深さを抑え、現実的な範囲に限定
type Repeat<S extends string, N extends number, Acc extends readonly unknown[] = []> =
  Acc["length"] extends N ? Acc[number] extends never ? "" : string : Repeat<S, N, [...Acc, S]>;

// 100以上は実用上ほぼ不要。型のためだけに深い再帰を組まない
type Y = Repeat<"a", 8>;

32. tsconfig関連エラー総まとめ

tsconfigの最新ベストプラクティスをまとめておきます。これを土台にすればエラーの大半は予防可能です。

32-1. Web(Vite/React)向け推奨設定

{
  "compilerOptions": {
    "target": "ES2022",
    "lib": ["ES2022", "DOM", "DOM.Iterable"],
    "module": "ESNext",
    "moduleResolution": "bundler",
    "jsx": "react-jsx",
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "exactOptionalPropertyTypes": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "verbatimModuleSyntax": true,
    "isolatedModules": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "noEmit": true,
    "resolveJsonModule": true,
    "baseUrl": ".",
    "paths": { "@/*": ["src/*"] }
  },
  "include": ["src"]
}

32-2. Node(サーバー)向け推奨設定

{
  "compilerOptions": {
    "target": "ES2022",
    "lib": ["ES2022"],
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "outDir": "dist",
    "rootDir": "src",
    "types": ["node"]
  },
  "include": ["src"]
}

33. moduleResolution: bundler 移行時のエラー対処

Vite/Next.js/モダンバンドラ環境では moduleResolution: "bundler" が推奨ですが、旧コードからの移行でエラーが出ることがあります。

33-1. 拡張子付きimport要求エラー

Before:

// node モジュールでは動くが bundler モードでエラーになることがある
import { sum } from "./math";  // 拡張子なし

After:

// ✅ bundler モードに切り替える(拡張子未指定OK)
{
  "compilerOptions": {
    "module": "ESNext",
    "moduleResolution": "bundler",
    "allowImportingTsExtensions": false
  }
}

33-2. .ts拡張子つきimport(bundler 限定)

Before:

// ❌ NodeNextでは禁止される .ts 直接import
import { sum } from "./math.ts";

After:

// ✅ bundler モードかつ allowImportingTsExtensions: true で許可
{
  "compilerOptions": {
    "module": "ESNext",
    "moduleResolution": "bundler",
    "allowImportingTsExtensions": true,
    "noEmit": true
  }
}

34. 補助: ESLint/TypeScript Eslint 連携エラー

TS本体エラーではないが、CIで一緒に落ちる定番の組み合わせ。

34-1. @typescript-eslint パーサ未設定

Before:

// .eslintrc.cjs
module.exports = {
  // ❌ TS構文がパースできない
  extends: ["eslint:recommended"],
};

After:

// .eslintrc.cjs
module.exports = {
  parser: "@typescript-eslint/parser",
  plugins: ["@typescript-eslint"],
  extends: [
    "eslint:recommended",
    "plugin:@typescript-eslint/recommended",
  ],
  parserOptions: {
    project: "./tsconfig.json",
    tsconfigRootDir: __dirname,
  },
};

エラーをそもそも減らすための7つの設計指針

個別エラーを潰し続けるだけでなく、再発を抑える設計面の工夫も重要です。

  1. strictは最初から全部ON。後から有効化するとリファクタが地獄になる。
  2. any禁止、unknownで受ける。catch・JSON・外部APIはunknownから絞り込む。
  3. 判別可能ユニオンで型を分岐。{ ok: true; data } | { ok: false; error } パターンを徹底。
  4. Zod等のスキーマライブラリで「型と実行時を一致」させる。
  5. satisfies演算子で「型に合致しつつ実態の精度を保つ」を活用。
  6. noUncheckedIndexedAccessを有効に。配列・Recordのundefined見落としを防止。
  7. tsconfigの定期点検。半年に1回はテンプレを最新化する。

satisfies の実用パターン

多くのエラーは satisfies で予防的に検知できます。

// 型は合っているか? を確認しつつ、実態の文字列リテラル型を温存する
type Status = "idle" | "loading" | "success" | "error";

// const assertion + satisfies で「ユニオンに属するか」+「リテラル温存」両立
const initial = "idle" as const satisfies Status;
// 型は "idle"(リテラル)、しかも Status の一員であることが保証される
// ルートマップで使うと便利
const routes = {
  home: "/",
  posts: "/posts",
  about: "/about",
} as const satisfies Record<string, `/${string}`>;

type Route = (typeof routes)[keyof typeof routes];
// "/" | "/posts" | "/about"

1日でエラー耐性を底上げする学習ルート

ここまで30+パターン見てきましたが、エラーメッセージを「読める」状態にするには、独学だけでなく体系学習も有効です。型システムの背景・実務でのアンチパターン・現場でのレビュー文化まで含めて学べる選択肢を挙げておきます。

  • テックアカデミー: TypeScript+Reactをマンツーマンメンタリングで詰められる。エラー解決の壁打ち相手として最適。
  • 侍エンジニア: 現役エンジニア講師の個別カリキュラム。実務想定のコードレビュー量が多い。
  • DMM WEBCAMP: チーム開発でのレビュー経験を積みたい人向け。Git運用も学べる。
  • レバテックキャリア: TypeScript案件専門のキャリア相談。スクール卒業後の実戦の場としても有効。

よくある質問(FAQ)

Q1. anyを使えばエラーは消えますが、本当にダメですか?

短期的には消えますが、後で必ず実行時バグになって返ってきます。一時しのぎなら // @ts-expect-error 理由を書く の方が安全です(理由のコメントが必須化されるため)。

Q2. 非nullアサーション(!)はどこまで許容できますか?

「絶対にnullにならないと自分で証明できる場所」だけにとどめましょう。useRefの初期化直後、document.querySelectorでセレクタが固定の場合などです。配列要素アクセスやAPIレスポンスでは絶対に使ってはいけません。

Q3. エラーが多すぎて手をつけられない既存プロジェクト、どう移行しますか?

順番は (1) skipLibCheck: true でnode_modules関連を無効化、(2) strict: false で起動、(3) ファイル単位で // @ts-check を付けて部分strict化、(4) 段階的に noImplicitAnystrictNullChecksstrict の順に有効化、です。

Q4. TS2589 が出たら型を諦めるしかありませんか?

多くは型再帰の打ち切り条件を入れれば収まります。それでも収まらない場合は、型ではなく実行時(関数・パラメータ)で表現する方が現実的です。型は読めなくなった瞬間に負債になります。

Q5. verbatimModuleSyntax は有効にすべき?

新規プロジェクトでは 有効推奨import type の徹底でバンドルサイズに優しく、二重import問題も防げます。

まとめ

TypeScriptのエラーは「言語が口うるさい」のではなく、本番で死ぬ前にコンパイラが代わりに死んでくれているとも言えます。本記事の30+パターンをBefore/Afterで身体に染み込ませれば、エラー解決速度は劇的に上がります。

大事なのは以下の3点です。

  • エラーコードを読む癖をつける(TS+4桁を必ず控えてからググる)
  • 絞り込みで解決する(! / as は最終手段)
  • tsconfigを最新ベストプラクティスに保つ(半年に1度の点検を)

この記事をブックマークしておき、エラーに遭遇したらCtrl+Fでコード番号を検索する使い方をおすすめします。最強の自分専用リファレンスとしてどうぞ。

コメント

タイトルとURLをコピーしました