何が起きたのか
TypeScriptプロジェクトを進めていると、コンパイラーまたはVS Codeの赤い波線がこんなエラーを投げてきます:
Type 'string' is not assignable to type 'number'. ts(2322)
ts(2322)というコードは型の不一致を意味します。つまり、数値を期待している場所に文字列を渡しているのです。TypeScriptで最もよく遭遇する型エラーといえますが、何を確認すべきかわかれば、ほぼ5分で解決できます。
よくあるシナリオ
このエラーの大半は4つのパターンに集約されます。これらを把握しておけば、詰まることはほとんどなくなります。
1. 文字列リテラルを数値変数に代入する
let age: number = "25"; // Error: Type 'string' is not assignable to type 'number'
典型的なケースです。変数はnumberを要求しているのに、文字列を渡しています。
2. 関数の戻り値の型の不一致
function getScore(): number {
return "100"; // Error: Type 'string' is not assignable to type 'number'
}
問題は同じですが、場所が違います。関数は数値を返すと宣言しているにもかかわらず、文字列を返しています。
3. オブジェクトプロパティの型の不一致
interface User {
id: number;
name: string;
}
const user: User = {
id: "abc123", // Error: Type 'string' is not assignable to type 'number'
name: "Alice",
};
4. APIレスポンスやフォーム入力(非常によくあるケース)
// フォームの入力値は常に文字列です — 例外はありません
const input = document.getElementById("age") as HTMLInputElement;
const age: number = input.value; // Error: 'string' not assignable to 'number'
多くの開発者がここでつまずきます。DOMは文字列を返します。TypeScriptは明示的な変換を求めています。
デバッグの方法
まず該当行を特定しましょう。VS Codeでは赤い下線にカーソルを合わせると、どの変数が競合しているか、その理由まで詳しく表示されます。ターミナルで確認したい場合は次のコマンドを実行してください:
npx tsc --noEmit
このコマンドは出力ファイルを生成せずにプロジェクト全体の型チェックを行います。すべてのts(2322)がファイルパスと行番号とともに表示されます。
問題の場所を特定したら、次の3つの質問で修正方針を絞り込みましょう:
- この変数は本来
stringとnumberのどちらであるべきか? - データはAPI、フォームフィールド、環境変数など外部ソースから来ているか?
- 最近インターフェースや関数のシグネチャを変更した人はいるか?
解決策
修正1:文字列を数値に変換する
値が文字列だが確実に数値が必要な場合は、明示的に変換します。JavaScriptには3つの方法があります:
// Number() — 整数と小数の両方に対応
const age: number = Number(input.value);
// parseInt — 小数部を切り捨てる。基数10を必ず指定すること
const count: number = parseInt(input.value, 10);
// parseFloat — 小数点以下を保持する
const price: number = parseFloat(input.value);
注意点として、Number("abc")は例外をスローせず、静かにNaNを返します。JavaScriptの仕様上これも数値ですが、数値演算を行うと問題が発生します。必ずバリデーションを行いましょう:
const age = Number(input.value);
if (isNaN(age)) {
throw new Error("Invalid age value");
}
修正2:文字列が正しい場合は型アノテーションを変更する
バグが値ではなくアノテーション側にある場合もあります。変数が実際に文字列を保持すべきなら、型を修正しましょう:
// 型を変更する
let id: string = "abc123";
// どちらの型も取りうる場合はユニオン型を使う
let id: string | number = "abc123";
修正3:関数の戻り値の型を修正する
// 方法A:正しい型を返す
function getScore(): number {
return 100; // 文字列ではなく実際の数値
}
// 方法B:戻り値の型アノテーションを更新する
function getScore(): string {
return "100";
}
修正4:実態に合わせてインターフェースを更新する
APIレスポンスは、インターフェースを書いた時点の想定と異なる形状になっていることがよくあります。データを無理やり合わせるのではなく、インターフェースを修正しましょう:
interface User {
id: string; // numberから変更 — APIは実際には文字列を送信している
name: string;
}
const user: User = {
id: "abc123", // 問題なし
name: "Alice",
};
修正5:環境変数のケースを適切に処理する
Node.jsでは、すべての環境変数はstring | undefinedです。例外はありません。そのため、.envファイルに3000と書いていても、process.env.PORTは決して数値にはなりません:
// これはエラーになります:
const port: number = process.env.PORT; // Type: string | undefined
// 修正 — 変換してフォールバック値を指定する:
const port: number = Number(process.env.PORT) || 3000;
データがAPIから来る場合
as MyInterfaceのような型アサーションはここでは罠になります。TypeScriptに信頼を強制しますが、実行時のチェックはまったく行われません。APIがレスポンスの形状を変えても、コードは静かに壊れていきます:
// 危険 — 実行時バリデーションなし
const data = response.json() as MyInterface;
// より安全 — zodは実行時に形状を検証し、問題があれば即座に例外をスローする
import { z } from "zod";
const UserSchema = z.object({
id: z.number(),
name: z.string(),
});
const user = UserSchema.parse(await response.json());
APIが数値を期待しているところにid: "abc123"を送ってきた場合、zodは明確なエラーメッセージとともに即座に例外をスローします。アプリ全体にNaNが伝播するよりずっと安全です。
修正の確認
コンパイラーを実行しましょう。出力がなければエラーはありません:
npx tsc --noEmit
VS Codeの赤い波線は保存した瞬間に消えるはずです。消えない場合はTSランゲージサーバーを再起動してください。コマンドパレット(Ctrl+Shift+P / Cmd+Shift+P)を開いて、TypeScript: Restart TS Serverを実行します。
まとめ
as anyや// @ts-ignoreは使わないでください。エラーを黙らせるだけで、後で必ず実行時バグになります。- フォーム入力、URLパラメーター、環境変数は常に文字列です。型付きの場所に渡す前に変換しましょう。
- インターフェースと実際のデータが一致しない場合は、データではなくインターフェースを修正してください。
- ts(2322)はTypeScriptが正しく機能している証拠です。本番環境でしか発覚しなかったはずのバグを、事前に捕捉してくれています。

