【TypeScript】TS7006の原因と解決方法|Parameter implicitly has an ‘any’ typeを完全解説

【TypeScript】TS7006の原因と解決方法|Parameter implicitly has an ‘any’ typeを完全解説 TypeScript

TypeScriptで開発中に突然現れる TS7006 エラー。ターミナルやエディタに赤い波線とともに表示される Parameter 'x' implicitly has an 'any' type というメッセージに戸惑った経験はありませんか?

TS7006は「関数のパラメータに型注釈がなく、TypeScriptが型を推論できないため、暗黙的に any 型になってしまう」ことを警告するエラーです。JavaScriptでは当たり前だった「引数に型を書かない」というコーディングスタイルが、TypeScriptでは許されないケースがあるのです。

このエラーは一見単純に見えますが、発生するシチュエーションは多岐にわたります。通常の関数パラメータはもちろん、イベントハンドラArrayメソッドのコールバックPromiseのthenチェーンExpressのreq/resReactのPropsクラスメソッドなど、あらゆる場面で遭遇します。

この記事では、TS7006エラーを発生パターン別に徹底分類し、それぞれについて実際のエラーメッセージNGコード原因の解説OKコード(修正例)を掲載します。エラーが出たらこの記事を辞書的に参照すれば、すぐに解決できるはずです。

この記事で学べること

  • TS7006エラーメッセージの読み方意味
  • noImplicitAnystrict モードの関係
  • 暗黙の any がなぜ危険なのか
  • 関数パラメータ(通常関数・アロー関数・コールバック・デストラクチャリング・rest)の修正方法
  • イベントハンドラ(addEventListener・React onClick/onChange)での正しい型付け
  • Array メソッド(map/filter/reduce/forEach/find/sort)での型推論と型付け
  • Promise・非同期処理(then/catch/async/setTimeout)での型付け
  • Express / Node.js(req/res/next/ミドルウェア)での型付け
  • React(Props・Hooks・カスタムフック・Context)での型付け
  • クラスメソッド(メソッド・コンストラクタ・getter/setter)での型付け
  • tsconfig.json の設定による制御(noImplicitAny / strict)
  • 型の付け方パターン集(プリミティブ・オブジェクト・ユニオン・ジェネリクス・unknown)
  • JavaScript → TypeScript 移行時の大量TS7006対処法

前提知識:この記事はTypeScriptの基本的な型の書き方を理解している方を対象としています。型の基本から学びたい方は【TypeScript】型の書き方 完全入門を先にお読みください。

スポンサーリンク
  1. TS7006エラーとは?
    1. エラーメッセージの読み方
    2. なぜTS7006エラーが発生するのか
    3. noImplicitAny の仕組み
    4. strict モードとの関係
    5. 暗黙の any が危険な理由
  2. 基本的なケースと解決方法
    1. 関数パラメータに型がない
    2. アロー関数のパラメータ
    3. コールバック関数のパラメータ
    4. デストラクチャリングパラメータ
    5. rest パラメータ(…args)
    6. オプショナルパラメータとデフォルト値
  3. イベントハンドラでのTS7006
    1. addEventListener のコールバック
    2. イベント型の選び方一覧表
    3. React のイベントハンドラ
    4. React イベント型一覧
    5. カスタムイベント
  4. Array メソッドでのTS7006
    1. 型推論が効くケースと効かないケース
    2. reduce のコールバック
    3. sort の比較関数
  5. Promise・非同期処理でのTS7006
    1. then/catch のコールバック
    2. async 関数のパラメータ
    3. Promise コンストラクタの resolve/reject
    4. setTimeout / setInterval のコールバック
  6. Express / Node.js でのTS7006
    1. req, res, next の型付け
    2. ミドルウェアの型付け
    3. エラーハンドラの型
  7. React でのTS7006
    1. コンポーネントの Props パラメータ
    2. useEffect / useMemo / useCallback のコールバック
    3. カスタムフックのパラメータ
    4. Context の値
  8. クラスメソッドでのTS7006
    1. メソッドのパラメータ
    2. コンストラクタのパラメータ
    3. getter / setter
    4. インターフェース実装とオーバーライド
  9. tsconfig.json の設定による制御
    1. noImplicitAny の ON/OFF
    2. strict モードとの関係(詳細)
    3. 段階的移行での設定
    4. // @ts-nocheck / @ts-ignore の使い方
  10. 型の付け方パターン集
    1. プリミティブ型
    2. オブジェクト型(interface / type)
    3. ユニオン型
    4. ジェネリクス関数
    5. unknown を使った安全な any 回避
    6. 型推論に頼れるケースの見極め方
  11. JavaScript → TypeScript 移行時のTS7006対策
    1. 大量のTS7006エラーへの対処法
    2. 段階的な型付け戦略
    3. any を一時的に使う場合の管理方法
    4. JSDoc コメントでの型付け
  12. まとめ
    1. TS7006 vs 関連エラーコードの違い
    2. 関連記事

TS7006エラーとは?

TS7006 は TypeScript コンパイラが出す「暗黙的 any 型」に関するエラーです。正式なエラーメッセージは次のとおりです。

error TS7006: Parameter ‘x’ implicitly has an ‘any’ type.

日本語に訳すと「パラメータ ‘x’ は暗黙的に ‘any’ 型を持っています」となります。つまり、関数のパラメータに型注釈が付いておらず、かつ TypeScript が文脈から型を推論できない場合に発生するエラーです。

エラーメッセージの読み方

TS7006のエラーメッセージは、一定のフォーマットに従っています。具体例で構造を確認しましょう。

エラーメッセージの例
src/utils.ts:5:22 - error TS7006: Parameter 'name' implicitly has an 'any' type.

5   function greet(name) {
                     ~~~~

このエラーメッセージは以下のパーツで構成されています。

パーツ 意味
ファイルパス src/utils.ts エラーが発生したファイル
行:列 5:22 エラー箇所(5行目の22文字目)
エラーコード TS7006 TypeScriptのエラー番号
Parameter ‘name’ name 問題のあるパラメータ名
implicitly has an ‘any’ type 型注釈がなく暗黙的にany型になる

覚え方:「Parameter ‘パラメータ名‘ implicitly has an ‘any’ type」=「このパラメータに型を書いてください」です。TypeScriptが「型が分からないから教えて」と言っているのです。

なぜTS7006エラーが発生するのか

TypeScriptには noImplicitAny というコンパイラオプションがあります。このオプションが true に設定されている場合、型注釈がなく、かつ文脈から型を推論できないパラメータがエラーになります。

JavaScriptでは関数パラメータに型を書く必要はありません。

JavaScript(エラーなし)
// JavaScript では問題なし
function greet(name) {
  return `Hello, ${name}!`;
}

しかしTypeScriptで noImplicitAny: true の場合、同じコードがエラーになります。

TypeScript(TS7006エラー)
// TypeScript: noImplicitAny が true の場合エラー
function greet(name) {  // TS7006: Parameter 'name' implicitly has an 'any' type.
  return `Hello, ${name}!`;
}

noImplicitAny の仕組み

noImplicitAny は TypeScript の tsconfig.json で設定するコンパイラオプションの一つです。このオプションの動作を整理すると次のようになります。

設定 noImplicitAny 型注釈なしのパラメータ 結果
A true 推論不可 TS7006エラー
B true 推論可能(コールバック等) エラーなし
C false 推論不可 エラーなし(暗黙 any)
D false 推論可能 エラーなし

ポイントは設定Bのケースです。noImplicitAny: true であっても、TypeScriptが文脈的型付け(Contextual Typing)によって型を推論できる場合はエラーになりません。例えば、Array.prototype.map() のコールバックは配列の要素型から推論されるため、型注釈なしでもOKです。

文脈的型付けにより推論されるケース
const numbers: number[] = [1, 2, 3];

// OK: n は number[] の要素なので number と推論される
const doubled = numbers.map(n => n * 2);

strict モードとの関係

tsconfig.json の "strict": true を設定すると、noImplicitAny を含む複数の厳格チェックオプションが一括で有効になります。

tsconfig.json
{
  "compilerOptions": {
    "strict": true   // 以下のオプションをすべて有効にする
    // "noImplicitAny": true,          ← TS7006 の原因
    // "strictNullChecks": true,
    // "strictFunctionTypes": true,
    // "strictBindCallApply": true,
    // "strictPropertyInitialization": true,
    // "noImplicitThis": true,
    // "alwaysStrict": true,
    // "useUnknownInCatchVariables": true
  }
}

注意:"strict": true を設定しているプロジェクトでは、noImplicitAny を個別に設定していなくても TS7006 が発生します。現在のほとんどの TypeScript プロジェクトは strict: true で開発されているため、TS7006は非常に頻繁に遭遇するエラーです。

暗黙の any が危険な理由

「型がないならとりあえず any にしてくれればいいのに」と思うかもしれません。しかし、暗黙の any はTypeScript の型安全を根本から壊す極めて危険な存在です。

暗黙の any が引き起こすバグの例
// noImplicitAny: false の場合、エラーなしで通る
function calculateTotal(price, quantity) {
  return price * quantity;
}

// 呼び出し側で間違った型を渡してもエラーにならない!
const total = calculateTotal("100", "3");
console.log(total); // "100" * "3" = NaN(実行時バグ!)

// さらに、存在しないメソッドを呼んでもエラーにならない
function processData(data) {
  data.toUpperCase();   // data が number でも型チェックを通る
  data.nonExistentMethod(); // 存在しないメソッドもOKになる
  data.foo.bar.baz;       // 深いプロパティアクセスもOK
}

暗黙の any を許可すると、上記のコードはコンパイル時にはエラーが出ず、実行時に初めてバグが発覚します。これではTypeScriptを使う意味がありません。noImplicitAny: true(またはstrict: true)は、このような危険を防ぐ最も基本的な型安全の砦なのです。

暗黙 any の危険性 具体例
型チェックの無効化 string を渡すべき場所に number を渡してもエラーにならない
存在しないプロパティへのアクセス data.foo.bar.baz のようなアクセスがコンパイルを通る
自動補完が効かない エディタの IntelliSense が any では候補を絞れない
リファクタリングが困難 型情報がないため「名前変更」などのリファクタリングが不完全になる
any の伝播 any を引数に取った関数の戻り値も any になり、any が連鎖的に広がる

ポイント:TS7006エラーは「うるさい」と感じるかもしれませんが、TypeScriptの型安全を守る重要なガードレールです。エラーを無効化するのではなく、適切な型を付けることで対処しましょう。

基本的なケースと解決方法

TS7006エラーが発生する最も基本的なケースから見ていきましょう。関数パラメータに型注釈がないパターンを、バリエーションごとに確認します。

関数パラメータに型がない

最もシンプルなケースです。通常の関数宣言でパラメータに型注釈を付けていない場合に発生します。

error TS7006: Parameter ‘name’ implicitly has an ‘any’ type.

NGコード
function greet(name) {  // TS7006
  return `Hello, ${name}!`;
}

function add(a, b) {  // TS7006: a と b の両方でエラー
  return a + b;
}

function printUser(user) {  // TS7006
  console.log(user.name, user.age);
}

原因:nameabuser のいずれにも型注釈がなく、TypeScriptが文脈から型を推論できないため、暗黙的に any 型になります。

OKコード(修正例)
function greet(name: string): string {
  return `Hello, ${name}!`;
}

function add(a: number, b: number): number {
  return a + b;
}

interface User {
  name: string;
  age: number;
}

function printUser(user: User): void {
  console.log(user.name, user.age);
}

アロー関数のパラメータ

アロー関数でも同様に、パラメータに型注釈がなければTS7006が発生します。ただし、変数に関数型の型注釈がある場合は文脈的型付けにより推論されるため、エラーになりません。

error TS7006: Parameter ”x” implicitly has an ”any” type.

NGコード
// 変数に型注釈がないアロー関数
const double = (x) => x * 2;  // TS7006

// 複数パラメータのアロー関数
const multiply = (a, b) => a * b;  // TS7006: a と b

// ブロック構文のアロー関数
const formatName = (first, last) => {  // TS7006: first と last
  return `${first} ${last}`;
};
OKコード(修正例1: パラメータに直接型注釈)
const double = (x: number): number => x * 2;

const multiply = (a: number, b: number): number => a * b;

const formatName = (first: string, last: string): string => {
  return `${first} ${last}`;
};
OKコード(修正例2: 変数に関数型を付ける)
// 変数に関数型の型注釈があるため、パラメータの型が推論される
const double: (x: number) => number = (x) => x * 2;  // OK

// type エイリアスを使う方法
type MathFn = (a: number, b: number) => number;
const multiply: MathFn = (a, b) => a * b;  // OK

ポイント:修正例2のように変数側に関数型を付ける方法は、パラメータ側の型注釈を省略できるため、コードがシンプルになります。同じ関数型を複数の関数で使い回す場合にも便利です。

コールバック関数のパラメータ

コールバック関数では、呼び出し元の関数の型定義によって文脈的型付けが行われるかどうかが決まります。

NGコード(自作関数にコールバックを渡す場合)
// callback パラメータに型がない
function fetchData(url, callback) {  // TS7006: url, callback
  // ...
}
OKコード(修正例)
// 方法1: 関数パラメータに型を付ける
function fetchData(url: string, callback: (data: unknown) => void): void {
  // ...
}

// コールバック内の data は unknown と推論される(型注釈不要)
fetchData("https://api.example.com", (data) => {  // OK
  console.log(data);
});

// 方法2: コールバック型を定義
type FetchCallback = (data: unknown, error?: Error) => void;

function fetchData2(url: string, callback: FetchCallback): void {
  // ...
}

デストラクチャリングパラメータ

オブジェクトのデストラクチャリングを関数パラメータで使う場合、型注釈の書き方に注意が必要です。

error TS7006: Parameter ”name” implicitly has an ”any” type.

NGコード
// デストラクチャリングに型がない
function printUser({ name, age }) {  // TS7006: name, age
  console.log(`${name} (${age}歳)`);
}
OKコード(修正例: interface を使う ← 推奨)
interface User {
  name: string;
  age: number;
}

function printUser({ name, age }: User): void {
  console.log(`${name} (${age}歳)`);
}

注意:デストラクチャリングの : は「リネーム」の構文と紛らわしいです。{ name: string } と書くと「name を string という変数に代入する」という意味になります。型注釈はデストラクチャリングパターン全体の後ろに書きます。

rest パラメータ(…args)

rest パラメータ(...args)も型注釈がなければTS7006が発生します。rest パラメータは配列型として型注釈を付けます。

NGコード → OKコード
// NG
function sum(...args) { }  // TS7006

// OK
function sum(...args: number[]): number {
  return args.reduce((acc, val) => acc + val, 0);
}

// 先頭パラメータと rest を組み合わせる
function log(message: string, ...args: unknown[]): void {
  console.log(message, ...args);
}

オプショナルパラメータとデフォルト値

デフォルト値がある場合はその値から型が推論されるため、エラーにならないケースもあります。ただし、? だけでは推論されません。

デフォルト値とオプショナル
// デフォルト値あり → 推論される → エラーなし
function greet(name = "World") { }  // OK: name は string

// オプショナル(?)だけ → 推論されない → エラー
function search(query?) { }  // TS7006

// 明示的に型を付ける
function search(query?: string): void { }  // OK

ポイント:デフォルト値がある場合でも、型注釈を明示的に付けることが推奨されます。デフォルト値の型が意図と異なる場合のバグを防げます。

イベントハンドラでのTS7006

フロントエンド開発で最も頻繁にTS7006が発生するのがイベントハンドラです。DOM APIの addEventListener や、Reactのイベントハンドラなど、イベントオブジェクトの型を正しく指定する方法を見ていきましょう。

addEventListener のコールバック

addEventListener は TypeScript の型定義が充実しているため、多くの場合は文脈的型付けで推論されます。しかし、コールバックを別の変数に切り出すとTS7006が発生します。

文脈的型付けが効くケース(エラーなし)
// addEventListener にインラインで渡す → 推論される
document.addEventListener("click", (event) => {
  // event は MouseEvent と推論される
  console.log(event.clientX, event.clientY);
});

document.addEventListener("keydown", (event) => {
  // event は KeyboardEvent と推論される
  console.log(event.key);
});

error TS7006: Parameter ”event” implicitly has an ”any” type.

NGコード(コールバックを変数に切り出す)
// 変数に切り出すと文脈的型付けが効かない
const handleClick = (event) => {  // TS7006
  console.log(event.clientX);
};
document.addEventListener("click", handleClick);
OKコード(修正例)
// 方法1: パラメータに型注釈
const handleClick = (event: MouseEvent) => {
  console.log(event.clientX, event.clientY);
};

// 方法2: 変数に関数型を付ける
const handleKeyDown: (event: KeyboardEvent) => void = (event) => {
  console.log(event.key);
};

イベント型の選び方一覧表

DOM イベントの型は非常に多いため、よく使われるイベントの型を一覧にまとめます。

イベント名 使用例
click / dblclick MouseEvent マウスクリック
mousedown / mouseup / mousemove MouseEvent マウス操作
keydown / keyup / keypress KeyboardEvent キーボード入力
input Event / InputEvent フォーム入力
change Event フォーム値変更
submit SubmitEvent フォーム送信
focus / blur FocusEvent フォーカス
scroll Event スクロール
resize UIEvent ウィンドウリサイズ
drag / drop DragEvent ドラッグ&ドロップ
touchstart / touchend TouchEvent タッチ操作
wheel WheelEvent マウスホイール

React のイベントハンドラ

Reactでは独自のSyntheticEvent(合成イベント)型を使います。DOMのイベント型をそのまま使うとエラーになる場合があるため注意が必要です。

error TS7006: Parameter ”e” implicitly has an ”any” type.

NGコード(React)
// ハンドラを変数に切り出すとTS7006
const handleClick = (e) => {  // TS7006
  console.log(e.currentTarget);
};

const handleChange = (e) => {  // TS7006
  console.log(e.target.value);
};
OKコード(React イベント型を使う)
import React from "react";

// 方法1: React.MouseEvent を使う
const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
  console.log(e.currentTarget);
};

// 方法2: React.ChangeEvent を使う
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
  console.log(e.target.value);
};

// 方法3: React.FormEvent を使う
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
  e.preventDefault();
};

React イベント型一覧

React イベント型 対応する JSX属性 ジェネリクス引数
React.MouseEvent onClick, onDoubleClick HTMLButtonElement
React.ChangeEvent onChange HTMLInputElement, HTMLSelectElement
React.FormEvent onSubmit HTMLFormElement
React.KeyboardEvent onKeyDown, onKeyUp HTMLInputElement
React.FocusEvent onFocus, onBlur HTMLInputElement
React.DragEvent onDrag, onDrop HTMLDivElement
React.TouchEvent onTouchStart, onTouchEnd HTMLElement

ポイント:ReactのイベントハンドラをJSXにインラインで書く場合は文脈的型付けが効くためTS7006は発生しません。別の変数に切り出す場合にのみ型注釈が必要です。

カスタムイベント

独自に定義したカスタムイベントを扱う場合は、CustomEvent 型を使います。

カスタムイベントの型付け
// カスタムイベントのペイロードを型で定義
interface NotificationPayload {
  message: string;
  level: "info" | "warn" | "error";
}

// CustomEvent のジェネリクスで detail の型を指定
const handleNotification = (event: CustomEvent<NotificationPayload>) => {
  console.log(event.detail.message);  // string
  console.log(event.detail.level);    // "info" | "warn" | "error"
};

Array メソッドでのTS7006

配列メソッド(map / filter / reduce / forEach 等)のコールバックは、配列の型が明確であれば文脈的型付けで推論されます。しかし、配列の型が不明確な場合やジェネリクスが絡む場合にTS7006が発生します。

型推論が効くケースと効かないケース

型推論が効くケース(エラーなし)
// 型付き配列 → コールバックの引数が推論される
const numbers: number[] = [1, 2, 3];
numbers.map(n => n * 2);       // OK: n は number
numbers.filter(n => n > 1);     // OK: n は number
numbers.forEach(n => console.log(n)); // OK: n は number

// リテラルから推論
const names = ["Alice", "Bob"];  // string[] と推論
names.map(name => name.toUpperCase());  // OK
型推論が効かないケース(TS7006)
// 空配列の問題: [] は never[] と推論される
const items = [];  // never[]
items.push("hello");  // TS2345: string は never に代入不可

// API レスポンスなど型が any の場合
const data = JSON.parse(response);  // any
data.map((item) => item.name);  // item は any(エラーにならないが危険)
OKコード(空配列に型を付ける)
// 空配列には明示的に型を付ける
const items: string[] = [];
items.push("hello");  // OK

// API レスポンスに型を付ける
interface ApiItem {
  name: string;
  id: number;
}
const data: ApiItem[] = JSON.parse(response);
data.map((item) => item.name);  // OK: item は ApiItem

reduce のコールバック

reduce は特にTS7006が発生しやすいメソッドです。初期値の型によってアキュムレータの型が決まるため、初期値の型が曖昧だとエラーになります。

reduce のパターン
const numbers = [1, 2, 3, 4, 5];

// OK: 初期値 0 から acc は number と推論
const sum = numbers.reduce((acc, val) => acc + val, 0);

// 初期値がオブジェクトの場合は型指定が必要
interface CountMap { [key: string]: number; }

const words = ["apple", "banana", "apple"];
const counts = words.reduce<CountMap>((acc, word) => {
  acc[word] = (acc[word] || 0) + 1;
  return acc;
}, {});

sort の比較関数

sort の比較関数は、配列の型から推論されるためTS7006は通常発生しません。しかし、型が不明確な配列では注意が必要です。

sort の比較関数
const users: { name: string; age: number }[] = [
  { name: "Alice", age: 30 },
  { name: "Bob", age: 25 },
];

// OK: a, b は { name: string; age: number } と推論
users.sort((a, b) => a.age - b.age);

// 比較関数を変数に切り出す場合は型が必要
const compareByAge = (a: { age: number }, b: { age: number }): number => {
  return a.age - b.age;
};

まとめ:Arrayメソッドのコールバックは、配列自体の型が明確であれば推論されるのでTS7006は発生しません。空配列 [] やAPIレスポンスなど型が不明確な場合は、配列に型注釈を付ければコールバックの型も自動的に推論されます。

Promise・非同期処理でのTS7006

非同期処理でも、コールバック関数のパラメータに型注釈がなければTS7006が発生します。then / catch / async / setTimeout など、よくあるパターンを見ていきましょう。

then/catch のコールバック

Promise.then() のコールバックは、Promise の型パラメータから推論されます。ただし、fetch の戻り値を .json() で取得した場合は any になるため注意が必要です。

Promise の型推論
// 型付きPromise → then のコールバックは推論される
const promise: Promise<string> = Promise.resolve("hello");
promise.then(value => {
  // value は string と推論される → エラーなし
  console.log(value.toUpperCase());
});

// fetch → json() は any を返す(危険!)
fetch("/api/users")
  .then(res => res.json())  // Promise<any>
  .then(data => {
    // data は any(TS7006にはならないが型安全でない)
    console.log(data.nonExistent);  // エラーにならない!
  });
OKコード(fetch に型を付ける)
interface User {
  id: number;
  name: string;
}

// 方法1: then で型アサーション
fetch("/api/users")
  .then(res => res.json() as Promise<User[]>)
  .then(users => {
    // users は User[] と推論される
    users.forEach(user => console.log(user.name));
  });

// 方法2: async/await + 型アサーション(推奨)
async function getUsers(): Promise<User[]> {
  const res = await fetch("/api/users");
  const data: User[] = await res.json();
  return data;
}

async 関数のパラメータ

async 関数のパラメータも通常の関数と同じルールです。型注釈がなければTS7006が発生します。

async 関数
// NG
async function fetchUser(id) {  // TS7006: id
  const res = await fetch(`/api/users/${id}`);
  return res.json();
}

// OK
async function fetchUser(id: number): Promise<User> {
  const res = await fetch(`/api/users/${id}`);
  return res.json();
}

Promise コンストラクタの resolve/reject

Promise コンストラクタ
// ジェネリクスで型を指定 → resolve/reject は推論される
const myPromise = new Promise<string>((resolve, reject) => {
  // resolve は (value: string) => void と推論
  resolve("success");
});

// 型パラメータなしだと unknown
const noType = new Promise((resolve) => {
  resolve("hello");  // resolve は (value: unknown) => void
});

setTimeout / setInterval のコールバック

setTimeout / setInterval のコールバックはパラメータを取らないため、通常はTS7006は発生しません。ただし、setInterval の戻り値の型に注意が必要です。

タイマーの型
// Node.js と ブラウザで戻り値の型が異なる
// ブラウザ: number
// Node.js: NodeJS.Timeout

// 互換性のある書き方
const timerId: ReturnType<typeof setTimeout> = setTimeout(() => {
  console.log("done");
}, 1000);

Express / Node.js でのTS7006

Express.js でサーバーサイド開発をしていると、ルートハンドラやミドルウェアの req / res / next でTS7006が頻発します。Express の型定義パッケージ @types/express を活用した正しい型付けを見ていきましょう。

前提:npm install --save-dev @types/express で型定義をインストール済みであることを前提とします。

req, res, next の型付け

error TS7006: Parameter ”req” implicitly has an ”any” type.
error TS7006: Parameter ”res” implicitly has an ”any” type.

NGコード
const express = require("express");
const app = express();

// req, res に型がない
app.get("/users", (req, res) => {  // TS7006: req, res
  res.json({ users: [] });
});
OKコード(修正例)
import express, { Request, Response, NextFunction } from "express";
const app = express();

// 方法1: パラメータに直接型注釈
app.get("/users", (req: Request, res: Response) => {
  res.json({ users: [] });
});

// 方法2: ジェネリクスで req.params / req.body の型を指定
interface UserParams { id: string; }
interface UserBody { name: string; email: string; }

app.post("/users/:id", (req: Request<UserParams, {}, UserBody>, res: Response) => {
  const userId = req.params.id;    // string
  const userName = req.body.name;  // string
  res.json({ id: userId, name: userName });
});

ミドルウェアの型付け

ミドルウェアの型付け
import { Request, Response, NextFunction } from "express";

// 認証ミドルウェア
const authMiddleware = (
  req: Request,
  res: Response,
  next: NextFunction
): void => {
  const token = req.headers.authorization;
  if (!token) {
    res.status(401).json({ error: "Unauthorized" });
    return;
  }
  next();
};

エラーハンドラの型

Expressのエラーハンドラは4つのパラメータ(err, req, res, next)を取ります。

エラーハンドラ
import { Request, Response, NextFunction, ErrorRequestHandler } from "express";

// 方法1: 各パラメータに型注釈
const errorHandler = (
  err: Error,
  req: Request,
  res: Response,
  next: NextFunction
): void => {
  console.error(err.stack);
  res.status(500).json({ error: "Internal Server Error" });
};

// 方法2: ErrorRequestHandler 型を使う
const errorHandler2: ErrorRequestHandler = (err, req, res, next) => {
  // すべてのパラメータが推論される
  res.status(500).json({ error: err.message });
};

ポイント:Express の RequestHandler / ErrorRequestHandler 型を変数に付けると、すべてのパラメータが自動的に推論されるため、個別の型注釈が不要になります。

React でのTS7006

React + TypeScript の開発では、コンポーネントの Props、Hooks のコールバック、カスタムフック、Context など、さまざまな場面でTS7006が発生します。React固有の型付けパターンを習得しましょう。

コンポーネントの Props パラメータ

関数コンポーネントの引数(Props)に型がないとTS7006が発生します。

NGコード
// Props に型がない
const UserCard = (props) => {  // TS7006: props
  return <div>{props.name}</div>;
};

// デストラクチャリングでも同様
const Greeting = ({ name, age }) => {  // TS7006: name, age
  return <p>{name} ({age}歳)</p>;
};
OKコード(修正例)
// 方法1: interface で Props を定義
interface UserCardProps {
  name: string;
  age: number;
  email?: string;  // オプショナル
}

const UserCard = (props: UserCardProps) => {
  return <div>{props.name}</div>;
};

// 方法2: デストラクチャリング + 型注釈
const Greeting = ({ name, age }: UserCardProps) => {
  return <p>{name} ({age}歳)</p>;
};

// 方法3: React.FC(非推奨だが既存コードでよく見る)
const UserCard2: React.FC<UserCardProps> = ({ name, age }) => {
  return <div>{name} ({age})</div>;
};

useEffect / useMemo / useCallback のコールバック

Hooks のコールバック関数内で別の関数を定義する場合、その関数のパラメータにTS7006が発生することがあります。

Hooks での型付け
import { useState, useCallback, useMemo } from "react";

// useState: ジェネリクスで型を指定
const [user, setUser] = useState<User | null>(null);

// useCallback: コールバックのパラメータに型を付ける
const handleSelect = useCallback((id: number) => {
  console.log(`Selected: ${id}`);
}, []);

// useMemo: 戻り値の型は推論される
const sortedUsers = useMemo(() => {
  return [...users].sort((a, b) => a.name.localeCompare(b.name));
}, [users]);

カスタムフックのパラメータ

カスタムフック
// NG: パラメータに型がない
function useFetch(url) {  // TS7006
  // ...
}

// OK: ジェネリクスで戻り値の型も指定
function useFetch<T>(url: string): {
  data: T | null;
  loading: boolean;
  error: Error | null;
} {
  const [data, setData] = useState<T | null>(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<Error | null>(null);
  // ... fetch logic ...
  return { data, loading, error };
}

// 使い方
const { data, loading } = useFetch<User[]>("/api/users");

Context の値

Context の型付け
import { createContext, useContext } from "react";

interface AuthContextType {
  user: User | null;
  login: (email: string, password: string) => Promise<void>;
  logout: () => void;
}

// ジェネリクスで型を指定
const AuthContext = createContext<AuthContextType | undefined>(undefined);

// カスタムフックで型安全に取得
function useAuth(): AuthContextType {
  const context = useContext(AuthContext);
  if (!context) {
    throw new Error("useAuth must be used within AuthProvider");
  }
  return context;
}

クラスメソッドでのTS7006

TypeScriptのクラスでも、メソッドやコンストラクタのパラメータに型注釈がなければTS7006が発生します。

メソッドのパラメータ

NGコード → OKコード
// NG
class Calculator {
  add(a, b) {  // TS7006: a, b
    return a + b;
  }
}

// OK
class Calculator {
  add(a: number, b: number): number {
    return a + b;
  }
}

コンストラクタのパラメータ

コンストラクタ
// NG
class User {
  constructor(name, age) {  // TS7006
    this.name = name;
    this.age = age;
  }
}

// OK: パラメータプロパティを使う(推奨)
class User {
  constructor(
    public name: string,
    public age: number
  ) {}
}

// OK: 通常のパターン
class User2 {
  private name: string;
  private age: number;

  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }
}

getter / setter

setter のパラメータにも型注釈が必要です。getter の戻り値は推論されますが、明示するのが推奨です。

getter / setter
class Temperature {
  private _celsius: number = 0;

  // getter: 戻り値は推論されるが明示推奨
  get celsius(): number {
    return this._celsius;
  }

  // setter: value に型が必要
  set celsius(value: number) {
    this._celsius = value;
  }
}

インターフェース実装とオーバーライド

implements でインターフェースを実装している場合や、extends で親クラスのメソッドをオーバーライドする場合でも、メソッドのパラメータには型注釈が必要です。インターフェースの実装では文脈的型付けが効きません。

interface 実装
interface Logger {
  log(message: string): void;
  error(message: string, code: number): void;
}

// NG: implements していても型注釈は必要
class ConsoleLogger implements Logger {
  log(message) { }  // TS7006
  error(message, code) { }  // TS7006
}

// OK: 型注釈を明示
class ConsoleLogger implements Logger {
  log(message: string): void {
    console.log(message);
  }
  error(message: string, code: number): void {
    console.error(`[${code}] ${message}`);
  }
}

注意:TypeScript 4.x以降では、implements によるクラスメソッドのパラメータ推論は行われません。これはTypeScriptの設計上の制約です。メソッドの型注釈は省略できません。

tsconfig.json の設定による制御

TS7006エラーの根本原因は noImplicitAny コンパイラオプションです。このセクションでは、tsconfig.json の設定でTS7006を制御する方法と、段階的にTypeScriptの厳格度を上げていく戦略を解説します。

noImplicitAny の ON/OFF

tsconfig.json
{
  "compilerOptions": {
    // TS7006 を無効にする(非推奨)
    "noImplicitAny": false

    // TS7006 を有効にする(推奨)
    "noImplicitAny": true
  }
}

注意:"strict": true を設定している場合、"noImplicitAny": false で個別にオーバーライドしない限り、noImplicitAny は自動的に true になります。

strict モードとの関係(詳細)

strict: true は以下のオプションを一括で有効にします。個別に無効化も可能です。

strict: true + 個別オーバーライド
{
  "compilerOptions": {
    "strict": true,

    // strict: true の中で noImplicitAny だけ無効化
    // (移行期間中の一時的な設定として有用)
    "noImplicitAny": false
  }
}

段階的移行での設定

既存のJavaScriptプロジェクトをTypeScriptに移行する場合、いきなり strict: true にすると大量のエラーが発生します。段階的に厳格度を上げていく戦略が有効です。

段階 設定 効果
Step 1 strict: false エラーなしでまずTS化を完了
Step 2 noImplicitAny: true パラメータの型を順次追加
Step 3 strictNullChecks: true null/undefined チェックを追加
Step 4 strict: true 完全な型安全を実現

// @ts-nocheck / @ts-ignore の使い方

特定のファイルや行だけエラーを無視したい場合は、コメントディレクティブを使えます。ただし、これらは一時的な回避策であり、恒久的に使うことは推奨されません。

コメントディレクティブ
// @ts-nocheck
// ↑ ファイル先頭に書くと、そのファイル全体の型チェックを無効化

// @ts-ignore
function legacyFunction(data) {  // この行のエラーを無視
  return data;
}

// @ts-expect-error ← より安全(エラーがないと逆にエラーになる)
function anotherLegacy(input) {
  return input;
}
ディレクティブ スコープ 推奨度
@ts-nocheck ファイル全体 非推奨(型チェック完全無効)
@ts-ignore 次の1行 注意(エラーが解消されても残る)
@ts-expect-error 次の1行 やむを得ない場合はこちら

ポイント:@ts-ignore より @ts-expect-error を使いましょう。@ts-expect-error は、その行にエラーがなくなったときに「不要なディレクティブ」としてエラーを出してくれるため、型を修正した後にコメントの消し忘れを防げます。

型の付け方パターン集

TS7006を解決するには「パラメータに適切な型を付ける」ことが必要です。このセクションでは、よく使う型の付け方パターンを一覧で紹介します。

プリミティブ型

プリミティブ型
function example(
  str: string,        // 文字列
  num: number,        // 数値
  bool: boolean,      // 真偽値
  big: bigint,        // BigInt
  sym: symbol,        // Symbol
  nul: null,          // null
  und: undefined,     // undefined
) {}

オブジェクト型(interface / type)

オブジェクト型
// interface(拡張可能、推奨)
interface User {
  id: number;
  name: string;
  email?: string;     // オプショナル
  readonly createdAt: Date;  // 読み取り専用
}

// type エイリアス(ユニオンやタプルに便利)
type Point = {
  x: number;
  y: number;
};

function processUser(user: User): void { }
function movePoint(point: Point, dx: number, dy: number): Point {
  return { x: point.x + dx, y: point.y + dy };
}

ユニオン型

ユニオン型
// 複数の型を受け入れる
function formatId(id: string | number): string {
  return String(id);
}

// リテラル型のユニオン
function setStatus(status: "active" | "inactive" | "pending"): void {
  // ...
}

// null許容型
function processValue(value: string | null | undefined): void {
  if (value != null) {
    console.log(value.toUpperCase());
  }
}

ジェネリクス関数

ジェネリクス
// 汎用的な関数
function identity<T>(value: T): T {
  return value;
}

// 制約付きジェネリクス
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

// 配列を受け取るジェネリクス
function first<T>(arr: T[]): T | undefined {
  return arr[0];
}

unknown を使った安全な any 回避

any の代わりに unknown を使うことで、型安全を保ちつつ任意の型を受け入れることができます。

unknown vs any
// NG: any は何でもできてしまう
function processAny(data: any): void {
  data.toUpperCase();  // エラーにならない(危険)
}

// OK: unknown は型を絞り込んでから使う
function processUnknown(data: unknown): void {
  // data.toUpperCase();  // TS2571: unknown にはプロパティがない

  // 型ガードで絞り込む
  if (typeof data === "string") {
    data.toUpperCase();  // OK: data は string
  }
}

型推論に頼れるケースの見極め方

すべてのパラメータに型注釈を書く必要はありません。文脈的型付けにより推論される場合は省略できます。

ケース 型注釈 理由
関数宣言のパラメータ 必要 推論されない
型付き配列の .map() コールバック 不要 配列の型から推論
addEventListener のインラインコールバック 不要 イベント名から推論
変数に切り出したコールバック 必要 文脈が切れる
デフォルト値ありのパラメータ 推奨 推論されるが明示が安全
Promise.then のコールバック 不要 Promise の型から推論

JavaScript → TypeScript 移行時のTS7006対策

既存のJavaScriptプロジェクトをTypeScriptに移行すると、noImplicitAny: true を有効にした途端に数百〜数千のTS7006エラーが発生することがあります。効率的に対処するための戦略を紹介します。

大量のTS7006エラーへの対処法

移行の推奨ステップ

  • Step 1: まず .js.ts にリネームする(noImplicitAny: false で)
  • Step 2: 明らかな型エラー(TS2322等)を修正する
  • Step 3: noImplicitAny: true に切り替える
  • Step 4: TS7006エラーを重要度の高いファイルから順に修正する
  • Step 5: 残りのファイルには一時的に @ts-nocheck を付け、後で対処する

段階的な型付け戦略

一度にすべてのパラメータに完璧な型を付ける必要はありません。段階的に型の精度を上げていく戦略が現実的です。

段階的な型付け
// Step 1: まず any で通す(最低限のTS7006解消)
function processData(data: any): any {
  return data.map((item: any) => item.value);
}

// Step 2: unknown で安全にする
function processData(data: unknown): unknown {
  if (!Array.isArray(data)) throw new Error("Expected array");
  return data;
}

// Step 3: 正確な型を定義
interface DataItem { value: number; label: string; }

function processData(data: DataItem[]): number[] {
  return data.map(item => item.value);
}

any を一時的に使う場合の管理方法

移行期間中に any を使う場合は、後で見つけやすいようにコメントでマークしておくのが重要です。

any の管理
// TODO: 型を修正する(移行完了後)
function legacyProcess(data: any): any { // FIXME: any
  return data;
}

// ESLint で any の使用を警告にする
// .eslintrc.json:
// "@typescript-eslint/no-explicit-any": "warn"

JSDoc コメントでの型付け

.js ファイルのまま型情報を追加したい場合は、JSDoc コメントを使えます。TypeScript は JSDoc を認識し、checkJs: true と組み合わせることで型チェックが可能です。

JSDoc による型付け(.js ファイル)
/**
 * ユーザーを挨拶する
 * @param {string} name - ユーザー名
 * @returns {string} 挨拶文
 */
function greet(name) {
  return `Hello, ${name}!`;
}

/**
 * @typedef {Object} User
 * @property {number} id
 * @property {string} name
 */

/**
 * @param {User} user
 * @returns {void}
 */
function printUser(user) {
  console.log(user.name);
}
tsconfig.json(JSDoc + 型チェック)
{
  "compilerOptions": {
    "allowJs": true,     // .js ファイルも処理
    "checkJs": true,     // .js ファイルも型チェック
    "noImplicitAny": true
  }
}

まとめ

TS7006エラー(Parameter implicitly has an ''any'' type)は、TypeScriptの noImplicitAny オプションが有効な場合に、型注釈のないパラメータで発生するエラーです。この記事の要点を振り返りましょう。

TS7006 解決のポイント

  • 基本原則:関数パラメータに型注釈を付ける。これが最もシンプルで確実な解決方法です。
  • 文脈的型付け:配列メソッドのコールバック、addEventListener のインラインコールバック、Promise.then のコールバックなどでは型推論が効くため型注釈は不要です。
  • 変数に切り出す場合は注意:コールバックを別変数に切り出すと文脈的型付けが効かなくなるため、パラメータに型注釈が必要です。
  • デストラクチャリング:パターン全体の後ろに型を書きます。interface / type を定義して使うのが推奨です。
  • React:React.MouseEvent / React.ChangeEvent 等の合成イベント型を使います。Props は interface で定義します。
  • Express:Request / Response / NextFunction@types/express から import します。
  • any より unknown:どうしても型が決められない場合は any ではなく unknown を使い、型ガードで絞り込みます。
  • 移行期間:@ts-expect-error や段階的な noImplicitAny 有効化で対応します。

TS7006 vs 関連エラーコードの違い

TS7006と混同しやすいエラーコードとの違いを整理します。

エラーコード メッセージ 原因
TS7006 Parameter implicitly has an ”any” type パラメータに型注釈がない
TS7031 Binding element implicitly has an ”any” type 束縛要素(デストラクチャリング)に型がない
TS2322 Type ”X” is not assignable to type ”Y” 型の不一致(代入先と代入元の型が合わない)
TS2339 Property ”X” does not exist on type ”Y” 存在しないプロパティにアクセス
TS2345 Argument of type ”X” is not assignable to type ”Y” 関数の引数の型が合わない

関連記事

TypeScriptの型システムをさらに深く学びたい方は、以下の記事もあわせてお読みください。

TypeScript 関連記事