TS7006とは、関数の引数に型注釈がなく、まわりの文脈からも型を決められないときに出るTypeScriptのエラーです。 エラー文は「Parameter 'x' implicitly has an 'any' type.」、日本語表示では「パラメーター 'x' の型は暗黙的に 'any' になります。」です。noImplicitAny(strict に含まれる設定)が有効なときに出ます。

直し方は「引数に型を書く」の一言ですが、同じ書き方でも、エラーになる場所とならない場所があります。prices.map((p) => ...) は通るのに、関数を変数に入れてから渡すと TS7006 になる、といった違いです。この記事では「どこの引数か」で場面を分け、推論される書き方とされない書き方を同じコードで tsc にかけて、出力全文と型の付け方を示します。

確認環境:Ubuntu 24.04 / Node.js 22.22.2 / TypeScript 7.0.2(npx tsc --version の表示は Version 7.0.2)。既定値の比較には TypeScript 5.9.3 と 6.0.3 を使いました。

TS7006の早見表(どこの引数か→推論されるか→型の付け方)

どこの引数か そのまま書くと 型の付け方
通常の関数・アロー関数・メソッド TS7006 (a: number, b: number) と引数に書く
既定値つきの引数 (n = 1) 推論される 書かなくてよい(既定値から number と決まる)
map / filter / reduce に直接書くコールバック 推論される 書かなくてよい
関数を変数に入れてから map に渡す TS7006 引数に書く、または変数に関数型を書く
addEventListener に直接書くハンドラ 推論される 書かなくてよい
外に出したイベントハンドラ TS7006 (e: MouseEvent)、または HTMLElementEventMap["input"]
then / catch に直接書くコールバック 推論される 書かなくてよい
外に出した then / catch のコールバック TS7006 then は結果の型、catch は unknown
分割代入した引数 ({ name, price }) TS7031 分割代入の後ろに : { name: string; price: number }
残余引数 (...args) TS7019 (...args: string[])
型が Function のコールバック引数に渡す関数 TS7006 呼び出す側で書く、または型定義を具体的にする

「推論される」のは、関数を書いた場所が受け取る型を教えてくれる場合です。これを文脈による型付け(contextual typing)と呼びます。関数を別の場所で定義すると、その手がかりがなくなります。

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「TS7006「暗黙的にany型」引数の型の付け方をパターン別に解説」

確認に使った tsconfig とコマンド

{
  "compilerOptions": {
    "target": "es2022",
    "module": "nodenext",
    "strict": true,
    "noEmit": true,
    "lib": ["es2022", "dom"],
    "types": []
  }
}
npm i -D typescript@latest
npx tsc --noEmit -p .               # 英語のエラー文
npx tsc --noEmit -p . --locale ja   # 日本語のエラー文

通常の関数・アロー関数・メソッドの引数

もっとも基本の形です。関数宣言、アロー関数、オブジェクトのメソッド、クラスのメソッドのどれでも、型注釈のない引数は TS7006 になります。

function add(a, b) {
  return a + b;
}
const double = (n) => n * 2;
const obj = {
  greet(name) { return `こんにちは、${name}さん`; },
};
class Counter {
  count = 0;
  add(step) { this.count += step; }
}
function withDefault(n = 1) { return n; }
function rest(...args) { return args.length; }
01-function.ts(1,14): error TS7006: Parameter 'a' implicitly has an 'any' type.
01-function.ts(1,17): error TS7006: Parameter 'b' implicitly has an 'any' type.
01-function.ts(4,17): error TS7006: Parameter 'n' implicitly has an 'any' type.
01-function.ts(6,9): error TS7006: Parameter 'name' implicitly has an 'any' type.
01-function.ts(10,7): error TS7006: Parameter 'step' implicitly has an 'any' type.
01-function.ts(13,15): error TS7019: Rest parameter 'args' implicitly has an 'any[]' type.

日本語表示(--locale ja)では次のようになります。

01-function.ts(1,14): error TS7006: パラメーター 'a' の型は暗黙的に 'any' になります。

12行目の withDefault(n = 1) にはエラーが出ていません。既定値があれば、その値から型が決まるためです。残余引数(...args)は番号が変わって TS7019 になります。

型を書いた形です。node 01-function.ts で実行した出力も載せます。

function add(a: number, b: number) {
  return a + b;
}
const double = (n: number) => n * 2;
const obj = {
  greet(name: string) { return `こんにちは、${name}さん`; },
};
class Counter {
  count = 0;
  add(step: number) { this.count += step; }
}
function rest(...args: string[]) { return args.length; }

console.log(add(1, 2), double(4), obj.greet("田中"), rest("a", "b"));
3 8 こんにちは、田中さん 2

戻り値の型は、return の式から推論されるので書かなくても TS7006 にはなりません。

map・filter・reduce のコールバック

配列のメソッドに直接書くコールバックは、配列の要素の型から引数の型が決まります。

const prices = [120, 80, 300];

// 文脈から推論される: エラーなし
const taxed = prices.map((p) => Math.round(p * 1.1));
const cheap = prices.filter((p) => p < 200);
const sum = prices.reduce((acc, p) => acc + p, 0);

// 関数を先に変数へ入れると、文脈がなくなる
const addTax = (p) => Math.round(p * 1.1);
const taxed2 = prices.map(addTax);

// any[] や型のない配列では、コールバックの引数も any になる(エラーは出ない)
const rows: any[] = JSON.parse("[]");
const names = rows.map((r) => r.name);
02-array.ts(9,17): error TS7006: Parameter 'p' implicitly has an 'any' type.

エラーは9行目だけです。4〜6行目と9行目は同じ (p) => ... ですが、9行目は「どこに渡されるか」がまだ決まっていない時点で関数を定義しているため、p の型の手がかりがありません。

13行目もエラーになりませんが、こちらは事情が違います。rows が any[] なので、r は「any と推論された」状態です。TS7006 は出ないまま型検査が働かなくなるので、配列の要素に型を付けておくと安心です。

const prices = [120, 80, 300];

// 直し方1: 引数に型注釈を書く
const addTax = (p: number) => Math.round(p * 1.1);

// 直し方2: 変数の側に関数型を書く(引数は推論される)
const half: (p: number) => number = (p) => p / 2;

console.log(prices.map(addTax), prices.map(half));

// any[] は要素の型を付けると、コールバックの引数にも型が届く
type Row = { name: string };
const rows: Row[] = JSON.parse('[{"name":"ノート"}]');
console.log(rows.map((r) => r.name));
[ 132, 88, 330 ] [ 60, 40, 150 ]
[ 'ノート' ]

イベントハンドラの引数

addEventListener に直接書けば、イベント名("click" など)からイベントの型が決まります。関数を外に出すと TS7006 になります。

const button = document.querySelector("button")!;
const input = document.querySelector("input")!;

// addEventListener に直接書く: 推論される
button.addEventListener("click", (e) => console.log(e.clientX));
input.addEventListener("input", (e) => console.log(e.target));

// 関数を外に出す: 文脈がなくなる
function onClick(e) {
  console.log(e.clientX);
}
button.addEventListener("click", onClick);

const onInput = (e) => console.log(e.target);
input.addEventListener("input", onInput);
03-event.ts(9,18): error TS7006: Parameter 'e' implicitly has an 'any' type.
03-event.ts(14,18): error TS7006: Parameter 'e' implicitly has an 'any' type.

外に出したハンドラには、イベントの型を書きます。型の名前が分からないときは、HTMLElementEventMap["イベント名"] と書くとイベント名から引けます。次のコードは tsc でエラーなしを確認しています。

const button = document.querySelector("button")!;
const input = document.querySelector("input")!;

// 直し方1: イベントの型を書く
function onClick(e: MouseEvent) {
  console.log(e.clientX);
}
button.addEventListener("click", onClick);

// 直し方2: イベント名から型を引く
const onInput = (e: HTMLElementEventMap["input"]) => console.log(e.target);
input.addEventListener("input", onInput);

// 直し方3: 関数全体の型を書く
const onKey: (e: KeyboardEvent) => void = (e) => console.log(e.key);
input.addEventListener("keydown", onKey);
イベント名 よく使う型
click、mousedown、mousemove MouseEvent
keydown、keyup KeyboardEvent
input、change Event
submit SubmitEvent
focus、blur FocusEvent

Promise の then・catch のコールバック

type User = { id: number; name: string };

async function fetchUser(id: number): Promise<User> {
  return { id, name: "田中" };
}

// 推論される
fetchUser(1).then((user) => console.log(user.name));
fetchUser(1).catch((err) => console.error(err));

// Promise を自分で作る: resolve / reject は推論される
const wait = new Promise<string>((resolve, reject) => setTimeout(() => resolve("ok"), 10));

// 関数を外に出す
const show = (user) => console.log(user.name);
fetchUser(1).then(show);

function handleError(err) {
  console.error(err);
}
fetchUser(1).catch(handleError);
04-promise.ts(15,15): error TS7006: Parameter 'user' implicitly has an 'any' type.
04-promise.ts(18,22): error TS7006: Parameter 'err' implicitly has an 'any' type.

then のコールバックには Promise が返す値の型(ここでは User)を書きます。catch に届く値は Error とは限らないので、unknown で受けて instanceof で確かめる書き方にすると、どんな値が届いても扱えます。

type User = { id: number; name: string };

async function fetchUser(id: number): Promise<User> {
  return { id, name: "田中" };
}

const show = (user: User) => console.log(user.name);
fetchUser(1).then(show);

// catch に届く値は何でもあり得るので unknown で受けて確かめる
function handleError(err: unknown) {
  console.error(err instanceof Error ? err.message : String(err));
}
fetchUser(1).catch(handleError);
Promise.reject(new Error("接続できませんでした")).catch(handleError);
田中
接続できませんでした

分割代入した引数(TS7031になる)

引数を { name, price } のように分割代入で受け取ると、番号は TS7031 に変わり、文言も「Binding element」になります。

function label({ name, price }) {
  return `${name}: ${price}円`;
}
const area = ([w, h]) => w * h;
function open({ url, retry = 3 }) {
  return `${url} (${retry})`;
}
05-destructure.ts(1,18): error TS7031: Binding element 'name' implicitly has an 'any' type.
05-destructure.ts(1,24): error TS7031: Binding element 'price' implicitly has an 'any' type.
05-destructure.ts(4,16): error TS7031: Binding element 'w' implicitly has an 'any' type.
05-destructure.ts(4,19): error TS7031: Binding element 'h' implicitly has an 'any' type.
05-destructure.ts(5,17): error TS7031: Binding element 'url' implicitly has an 'any' type.

5行目の retry = 3 は、既定値から型が決まるのでエラーになっていません。

型は、分割代入の波かっこの後ろに、オブジェクト全体の型として書きます。

// 分割代入の「後ろ」にオブジェクト全体の型を書く
function label({ name, price }: { name: string; price: number }) {
  return `${name}: ${price}円`;
}

// 型に名前を付けると読みやすい
type OpenOptions = { url: string; retry?: number };
function open({ url, retry = 3 }: OpenOptions) {
  return `${url} (${retry})`;
}

// 配列の分割代入はタプル型で書く
const area = ([w, h]: [number, number]) => w * h;

console.log(label({ name: "ノート", price: 120 }), open({ url: "/items" }), area([3, 4]));
ノート: 120円 /items (3) 12

波かっこの中に型を書くと別の意味になる

次の書き方は、型注釈のように見えて、実際は「name を string という名前の変数として受け取る」という別名の構文です。

// よくある書きまちがい: これは型注釈ではなく「別名を付ける」構文
function label({ name: string, price: number }) {
  return `${name}: ${price}円`;
}
05b-wrong.ts(2,24): error TS7031: Binding element 'string' implicitly has an 'any' type.
05b-wrong.ts(2,39): error TS7031: Binding element 'number' implicitly has an 'any' type.
05b-wrong.ts(3,22): error TS2304: Cannot find name 'price'.

「Binding element 'string'」のように、型の名前が変数名として出てきたら、この書きまちがいのサインです。なお、3行目の name にエラーが出ていないのは、lib に dom を入れているとグローバル変数 name が存在するためで、引数の name を指しているわけではありません。

型定義が Function になっているライブラリのコールバック

ライブラリの型定義でコールバックが Function 型になっていると、直接書いても引数の型は推論されません。次の型定義ファイルを例にします。

// legacy-lib.d.ts
export function on(event: string, handler: Function): void;
import { on } from "./legacy-lib.js";

on("data", (payload) => console.log(payload.id));
on("data", function (payload, meta) { console.log(payload, meta); });
06-lib.ts(3,13): error TS7006: Parameter 'payload' implicitly has an 'any' type.
06-lib.ts(4,22): error TS7006: Parameter 'payload' implicitly has an 'any' type.
06-lib.ts(4,31): error TS7006: Parameter 'meta' implicitly has an 'any' type.

Function は「何かの関数」という情報しか持たないため、引数の型を伝えられません。直し方は2つあります。

import { on } from "./legacy-lib.js";

type Payload = { id: number };

// 直し方1: 呼び出す側で引数に型を書く
on("data", (payload: Payload) => console.log(payload.id));

自分で型定義を書ける場合は、コールバックの形を具体的にすると、呼び出す側は何も書かずに済みます。

// legacy-lib.d.ts
export type Payload = { id: number };
export function on(event: "data", handler: (payload: Payload) => void): void;
export function on(event: string, handler: (...args: unknown[]) => void): void;
import { on } from "./legacy-lib.js";

// 型定義の側でコールバックの型を決めると、呼び出す側は推論される
on("data", (payload) => console.log(payload.id));
on("close", (code) => console.log(code));

どちらも tsc でエラーなしを確認しています。型定義ファイル自体がないモジュールを import した場合は、TS7006 ではなく次の TS7016 になります。

TS7006に近い番号との違い

「implicitly has an 'any' type」を含むエラーは複数あります。次のコードを1つのファイルにまとめて tsc にかけました。

// TS7005 / TS7034: 型が決まらない変数
let cache;
function fill() { cache = [1, 2]; }
function read() { return cache; }

// TS7031: 分割代入した引数
function show({ title }) { return title; }

// TS7019: 残余引数
function log(...items) { return items; }

// TS7053: 文字列のキーでオブジェクトを引く
const colors = { red: "#f00", blue: "#00f" };
function pick(key: string) { return colors[key]; }

// TS7015: 数値以外の添字
const list = [1, 2, 3];
function at(i: string) { return list[i]; }

// TS7010: 戻り値の型が決まらない
declare function external(id: number);

// TS2683: this の型が決まらない
function whoAmI() { return this.name; }

// TS7008: メンバー
class Box { value; }

// TS7016: 型定義のないモジュール
import something from "./no-types.js";
07-near.ts(2,5): error TS7034: Variable 'cache' implicitly has type 'any' in some locations where its type cannot be determined.
07-near.ts(4,26): error TS7005: Variable 'cache' implicitly has an 'any' type.
07-near.ts(7,17): error TS7031: Binding element 'title' implicitly has an 'any' type.
07-near.ts(10,14): error TS7019: Rest parameter 'items' implicitly has an 'any[]' type.
07-near.ts(14,37): error TS7053: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ red: string; blue: string; }'.
  No index signature with a parameter of type 'string' was found on type '{ red: string; blue: string; }'.
07-near.ts(18,38): error TS7015: Element implicitly has an 'any' type because index expression is not of type 'number'.
07-near.ts(21,18): error TS7010: 'external', which lacks return-type annotation, implicitly has an 'any' return type.
07-near.ts(24,28): error TS2683: 'this' implicitly has type 'any' because it does not have a type annotation.
07-near.ts(27,13): error TS7008: Member 'value' implicitly has an 'any' type.
07-near.ts(30,23): error TS7016: Could not find a declaration file for module './no-types.js'. '/home/taro/app/no-types.js' implicitly has an 'any' type.

(最後の行のパスは環境に合わせて置き換えています。)

番号 対象 日本語のエラー文(--locale ja の出力。長いものは前半) 直し方
TS7006 引数 パラメーター 'a' の型は暗黙的に 'any' になります。 引数に型を書く
TS7031 分割代入した引数 バインド要素 'title' には暗黙的に 'any' 型が含まれます。 分割代入の後ろに型を書く
TS7019 残余引数 (英語)Rest parameter 'items' implicitly has an 'any[]' type. ...items: string[]
TS7034 / TS7005 型注釈も初期値もない変数 変数 'cache' の型は暗黙的に 'any' になります。 let cache: number[] = [] のように型か初期値を書く
TS7053 文字列のキーでの参照 型 'string' の式を使用して型 '…' にインデックスを付けることはできないため、要素は暗黙的に 'any' 型になります。 引数を key: keyof typeof colors にする
TS7015 数値以外の配列添字 インデックス式が型 'number' ではないため、要素に 'any' 型が暗黙的に指定されます。 添字を number にする
TS7010 戻り値の型がない宣言 'external' には戻り値の型の注釈がないため、戻り値の型は暗黙的に 'any' になります。 戻り値の型を書く
TS7008 型も初期値もないクラスのメンバー メンバー 'value' の型は暗黙的に 'any' になります。 value: string や初期値を書く
TS2683 this 'this' は型として注釈を持たないため、暗黙的に型 'any' になります。 function whoAmI(this: { name: string }) と書く
TS7016 型定義のないモジュール モジュール './no-types.js' の宣言ファイルが見つかりませんでした。 型定義パッケージを入れる、または .d.ts を書く

noImplicitAny と strict の既定値(5.9・6.0・7.0 の比較)

TS7006 は noImplicitAny が有効なときに出ます。noImplicitAny は strict を true にすると一緒に有効になります。では、tsconfig.json に何も書かなかった場合はどうなるでしょうか。中身が {} だけの tsconfig.json と、次の add.ts を置いて、3つの版で tsc --noEmit を実行しました。

function add(a, b) {
  return a + b;
}
console.log(add(1, 2));
版 tsconfig.json が {} のとき "strict": false と書いたとき
5.9.3 エラーなし(終了コード 0) エラーなし
6.0.3 TS7006 が2件(終了コード 2) エラーなし
7.0.2 TS7006 が2件(終了コード 1) エラーなし

6.0.3 と 7.0.2 の出力です。

add.ts(1,14): error TS7006: Parameter 'a' implicitly has an 'any' type.
add.ts(1,17): error TS7006: Parameter 'b' implicitly has an 'any' type.

strict を書かなかったときの扱いが、5.9.3 では無効、6.0.3 以降では有効という結果です。TypeScript 6.0 のリリースノートにも「strict is now true by default」と書かれています。版を上げたあとに TS7006 が急に増えた場合は、この既定値の違いが理由です。

tsc --showConfig でも確かめました。3つの版とも、{} の tsconfig.json に対する表示は次のとおりで、書いていない既定値は表示されません。

{
    "compilerOptions": {},
    "files": [
        "./add.ts"
    ]
}

そのため、いま strict が有効かどうかを showConfig だけで判断することはできません。tsconfig.json に "strict": true を明記しておくと、版による違いを受けず、読む人にも設定が伝わります。なお tsc --init が作る tsconfig.json には、3つの版とも "strict": true が入っていました。

設定の組み合わせも 7.0.2 で確かめました。

tsconfig の指定 add.ts の結果
"strict": true TS7006
"strict": false エラーなし
"strict": false, "noImplicitAny": true TS7006
"strict": true, "noImplicitAny": false エラーなし

個別の指定(noImplicitAny)が strict より優先されます。既存のコードに型を少しずつ付けていく途中であれば、"strict": true, "noImplicitAny": false で始めて、引数の型がそろった段階で noImplicitAny の行を外す、という進め方ができます。

型の付け方を選ぶ判断表

状況 選ぶ書き方
引数の型がはっきりしている 引数に直接書く:(p: number) =>
同じ形のコールバックを何度も書く 関数型に名前を付ける:type Handler = (e: MouseEvent) => void
その場でしか使わないコールバック メソッドに直接書いて推論に任せる
受け取る値の形がまだ分からない unknown で受けて、typeof や instanceof で確かめる
オブジェクトを分割代入で受ける 型に名前を付けて、分割代入の後ろに書く

引数に : any と書いてもエラーは消えますが、その引数を使う式はすべて型検査の対象から外れます。形が分からない値は unknown で受けると、使う前に確かめる書き方へ自然に導かれます。

よくある質問

Q. map の中では型を書かなくても通るのに、関数を外に出すとエラーになるのはなぜですか? A. map に直接書いた関数は、配列の要素の型から引数の型が決まります。外に出した関数は、定義した時点ではどこに渡されるかが分からないため、型の手がかりがありません。引数に型を書くか、変数の側に (p: number) => number のような関数型を書くと解決します。

Q. TypeScript の版を上げたら TS7006 が大量に出るようになりました。 A. tsconfig.json に strict を書いていない場合、5.9.3 では無効、6.0.3 以降では有効として扱われます。まず "strict": true を明記し、型を付ける作業を分けて進めたいときは "noImplicitAny": false を一時的に添えると、TS7006 だけを後回しにできます。

Q. TS7006 と TS7031 は何が違いますか? A. 対象が違います。TS7006 は (a) のような引数そのもの、TS7031 は ({ name }) のように分割代入で受け取った要素です。TS7031 は、分割代入の後ろにオブジェクト全体の型を書いて直します。

Q. イベントハンドラの引数の型名が分かりません。 A. HTMLElementEventMap["click"] のように、イベント名から型を引けます。または、いったん addEventListener に直接アロー関数を書き、エディタで引数にカーソルを合わせると推論された型名を確認できます。

まとめ

  • TS7006 は、引数に型注釈がなく、文脈からも型が決まらないときに出る
  • メソッドやイベント登録に直接書いたコールバックは推論される。関数を外に出したり変数に入れたりすると手がかりがなくなる
  • 既定値のある引数は書かなくてよい。既定値から型が決まる
  • 分割代入は TS7031、残余引数は TS7019 と番号が変わる。分割代入は波かっこの後ろに型を書く
  • コールバックの型が Function のライブラリでは推論されない。呼び出す側で書くか、型定義を具体的にする
  • strict を書かなかったときの扱いは、5.9.3 では無効、6.0.3・7.0.2 では有効。tsc --showConfig には既定値が出ないので、tsconfig.json に明記する
  • 形が分からない値は any ではなく unknown で受ける

参考・出典

  • Announcing TypeScript 6.0(Microsoft TypeScript ブログ、strict の既定値の変更):https://devblogs.microsoft.com/typescript/announcing-typescript-6-0/

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「TS7006「暗黙的にany型」引数の型の付け方をパターン別に解説」

株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?