- TypeScript
TS7006「暗黙的にany型」引数の型の付け方をパターン別に解説
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期連続の黒字決算。