エラーの内容
Argument of type 'string' is not assignable to parameter of type 'number'. ts(2345)
関数に値を渡したところ、型が一致しないためTypeScriptに拒否されました。十中八九、関数がnumberを要求しているところにstringを渡しています。ただし同様の型ミスマッチは、booleanとstring、nullとobjectなど、さまざまな組み合わせで発生します。
朗報があります。これはコンパイル時エラーです。TypeScriptがバグを本番環境に到達する前に検出してくれました。
発生する原因
TypeScriptはすべての関数呼び出しを宣言されたパラメーター型と照合します。number型のパラメーターにstringを渡すと — たとえ"42"であっても — コンパイラーは拒否します。値が数値に見えるかどうかは関係ありません。
よくある原因:
process.argv、req.query、URLSearchParamsからの読み取り — 値が"42"であっても、これらは常に文字列を返します。- HTMLの
<input>から値を取得する場合 —input.valueは例外なく常にstringです。 - JSONを解析した際に、フィールドが
10ではなく"10"として届いた場合。 numberとして始まった変数が、上流のどこかでstring | numberに拡張された場合。- ライブラリ関数に引数を誤った順序で渡した場合。
手順を追った修正方法
ステップ1 — ミスマッチを特定する
TypeScriptは正確な行を教えてくれます。その行を開き、渡している値を確認してから、関数シグネチャを確認しましょう。ズレはたいてい明白です:
// ts(2345)
function double(n: number): number {
return n * 2;
}
const input = "5"; // type: string
console.log(double(input)); // ❌ string → number mismatch
ステップ2 — 値を変換する
数値を表す文字列は、渡す前に解析する必要があります。
const input = "5";
console.log(double(Number(input))); // ✅ 明示的、小数も処理可能
console.log(double(parseInt(input, 10))); // ✅ 整数に最適 — 常に基数10を指定する
console.log(double(+input)); // ✅ 簡潔だが、コードレビューで見落としやすい
値が小数になる可能性がある場合はNumber()を使用します。整数が必要な場合はparseInt(input, 10)を使用します。チームのコードベースでは単項+は避けましょう — grepしにくく、一見するとタイポに見えます。
ステップ3 — よくある発生源に対処する
process.argv経由のCLI引数:
const raw = process.argv[2]; // string | undefined
const port = Number(raw);
if (isNaN(port)) throw new Error(`Invalid port: ${raw}`);
startServer(port); // ✅
HTMLの入力フィールド:
const input = document.getElementById("age") as HTMLInputElement;
const age = Number(input.value);
if (isNaN(age)) return; // 空文字列や "abc" も検出できる
setAge(age); // ✅
URLクエリパラメーター(Express / Fetch API):
// Express
app.get("/users", (req, res) => {
const page = Number(req.query.page); // req.query.page is string | string[] | ParsedQs
getUsers(page); // ✅
});
// URLSearchParams
const params = new URLSearchParams(location.search);
const id = Number(params.get("id")); // get() returns string | null
if (isNaN(id)) throw new Error("Missing id param");
fetchItem(id); // ✅
文字列型の数値を含むJSONペイロード:
// サーバーが { "count": 10 } ではなく { "count": "10" } を返した場合
const data = JSON.parse(response) as { count: string };
const count = Number(data.count);
renderList(count); // ✅
ステップ4 — ユニオン型をナローイングする
TypeScriptが変数をstring | numberに拡張することがあります。numberのみを受け付ける関数はそれでも拒否します。渡す前に型ガードを使ってナローイングしましょう。
function applyDiscount(price: number): number {
return price * 0.9;
}
const rawPrice: string | number = getPrice();
// オプションA: typeofでナローイング
if (typeof rawPrice === "number") {
applyDiscount(rawPrice); // ✅ TypeScriptはここでnumberと認識する
}
// オプションB: 無条件に変換
applyDiscount(Number(rawPrice)); // ✅ どちらの場合も機能する
ステップ5 — 代わりに関数シグネチャを修正する
呼び出し元は問題なく、シグネチャに問題がある場合もあります。関数が本当に文字列と数値の両方を扱うように設計されているなら、それを反映したパラメーター型に更新しましょう。
// 修正前 — 厳しすぎる
function log(value: number): void {
console.log(value);
}
// 修正後 — 両方を受け付ける
function log(value: number | string): void {
console.log(value);
}
関数が実際に両方を処理する場合のみ型を拡張してください。エラーを黙らせるためだけにやるのはNGです — それではTypeScriptの意味がなくなります。
修正を確認する
コンパイラーを実行して何も残っていないことを確認します:
# プロジェクト全体のチェック
npx tsc --noEmit
# 単一ファイル
npx tsc --noEmit src/yourfile.ts
# ts-nodeを使用
npx ts-node src/yourfile.ts
出力がなければ型エラーはゼロです。VS Codeでは、ファイルを保存した瞬間に赤い波線が消えます。
クイックリファレンス
Number(x)— 文字列・真偽値・nullを数値に変換する。解析できない場合はNaNを返すparseInt(x, 10)— 文字列から整数を取得する。末尾の非数値文字は無視される("42px"→42)parseFloat(x)— 文字列から小数を取得する。"3.14"などに便利+x—Number(x)の略記法。共有コードベースではgrepしにくいので避けること- 入力がユーザーや外部ソースからの場合は、必ず
isNaN()で検証すること
変換が間違った修正である場合
ts(2345)があちこちに表示されていますか?コードベース全体にNumber()の呼び出しを散りばめる前に、データモデルを確認してください。UUIDやMongoDBのObjectIDは本質的に文字列です — すべての呼び出し箇所で数値に変換しようとするのは的外れな戦いです。最初からstringとして型付けすれば、エラーは完全に消えます。ts(2345)エラーが広範囲に発生している場合は、各境界での変換の問題ではなく、データ層における型ミスマッチの症状であることがほとんどです。

