なぜ「型の変換」を理解すべきか?
みなさんは、こんな経験をしたことありませんか?
-
数値として加算したつもりが文字列として結合されていた
-
APIの戻り値を処理したら、想定外のエラーが出た
-
型をキャストしたはずなのに、実行時にクラッシュした
これらはすべて「型の扱い」や「型変換」による問題です。
型変換(キャスト)は、言語ごとにルールが異なり、知らないとすぐに落とし穴にはまってしまいます。
本記事では、Java・JavaScript・TypeScriptの3言語を使って、型の変換とその正しい使い方をわかりやすく徹底解説します。
型変換の基本と各言語での実装
JavaScript:柔軟すぎる型変換に要注意
例1:”5″ + 1 → “51”
console.log("5" + 1); // → "51"
解説
+ 演算子は数値同士なら加算を行いますが、どちらかが文字列であれば「文字列連結」に変わります。このコードでは “5” が文字列なので、1 は自動的に “1” に変換され、結果は “51” になります。JavaScriptでは暗黙的な型変換(implicit coercion)が多く、これが原因で意図しないバグを生むことがあります。
-
抑えるべきポイント:+ は「文字列優先」なので、混在する型の演算では注意!
-
注意点:数値加算のつもりで文字列連結になると、計算ロジックが破綻します。
例2:”5″ – 1 → 4
console.log("5" - 1); // → 4
解説
この例では – 演算子が使われていますが、- は数値演算のみを行います。そのため、JavaScriptは “5” を暗黙的に数値へ変換し、結果として 5 – 1 = 4 になります。このように、演算子によって型変換のルールが変わるのがJavaScriptの厄介なところです。
-
抑えるべきポイント:-, *, / は常に数値演算。文字列も数値に変換される。
-
注意点:文字列に数値以外の文字が含まれると NaN になります。
例3:Number(“42”) → 42
let str = "42";
let num = Number(str); // → 42
解説
この例では、明示的な型変換(explicit conversion)として Number() 関数を使っています。”42″ は数値の形をしているので、Number(“42”) は 42 という数値になります。明示的に型変換することで、意図しない挙動を防ぎ、コードの可読性と保守性が向上します。
-
抑えるべきポイント:暗黙変換を避け、明示的に Number(), String() を使うこと。
-
注意点:変換できない文字列(例:”abc”)は NaN になります。
例4:Boolean(“0”) → true
console.log(Boolean("0")); // → true
解説
JavaScriptでは、空文字列 “” や 0 は false として扱われますが、”0″(文字列のゼロ)は「空ではない文字列」なので true になります。ここでの落とし穴は、直感的には 0 → false と考えたくなる点です。文字列 “false” ですら真と評価されるので、厳密なチェックが求められます。
-
抑えるべきポイント:Boolean(value) で変換される基準を知る
-
注意点:”0″, “false” も真(truthy)として評価される!
Java:キャストと例外に要注意
例1:(int) 3.99 → 3
double d = 3.99;
int i = (int) d;
解説
この例は「精度を下げる方向への変換(Narrowing conversion)」で、Javaでは明示的なキャストが必要です。double から int へ変換すると、小数点以下は切り捨てられます。意図的なキャストであっても、精度が落ちることは仕様なので、注意が必要です。
-
抑えるべきポイント:Narrowing(精度を下げる)には必ずキャスト (int) が必要
-
注意点:小数点は切り捨て(四捨五入ではない)
例2:Integer.parseInt(“123”) → 123
String input = "123";
int value = Integer.parseInt(input);
解説
この例では文字列から整数への変換を行っています。Javaでは、parseInt() を使って文字列を数値に変換できます。ただし、変換対象の文字列が数値でないと NumberFormatException が発生するため、事前バリデーションが重要です。
-
抑えるべきポイント:文字列から数値への変換には parseInt(), parseDouble() を使う
-
注意点:不正な文字列(例:”abc”)は例外をスローします
例3:(String) obj → ClassCastException
Object obj = 42;
String s = (String) obj; // 実行時例外
解説
この例では、Object 型から String 型へキャストしようとしていますが、実体が Integer であるため ClassCastException が発生します。Javaのキャストは「見かけの型」だけではなく、実体型も一致していなければならない点が非常に重要です。
-
抑えるべきポイント:キャスト前には instanceof を使って実体を確認
-
注意点:誤ったキャストは実行時例外で落ちます(コンパイルは通る)
TypeScript:型安全とキャストの両立がカギ
例1:(value as string).length
let value: any = "hello";
let len = (value as string).length;
解説
この例では、value の型が any であるため、TypeScript側では型情報が不明です。そのため、開発者が「これは string である」と明示する必要があります。これを 型アサーション(型キャスト) と呼びます。これにより補完やエラー防止が効くようになります。
-
抑えるべきポイント:any の値を扱うときは型アサーションで補強
-
注意点:実際の型が違っていると実行時バグにつながる
例2:DOM操作のキャスト
const input = document.querySelector("#email") as HTMLInputElement;
console.log(input.value);
解説
TypeScriptで querySelector を使うと、返り値は Element | null 型になります。そのため、HTMLInputElement であることを明示することで、.value プロパティを正しく利用できます。キャストを使わないと補完も効かず、型エラーにもなります。
-
抑えるべきポイント:DOM要素の取得後は明示的に要素型をキャストする
-
注意点:null かもしれないので、キャスト後に null チェックを入れるのが理想
例3:Union型+型ガード
type Dog = { bark: () => void };
type Cat = { meow: () => void };
function speak(animal: Dog | Cat) {
if ("bark" in animal) {
animal.bark();
} else {
animal.meow();
}
}
解説
この例では Dog | Cat の Union 型を受け取り、in 演算子を使って型を絞り込んでいます。このような方法を 型ガード(Type Guard) と呼び、キャストなしで安全にオブジェクトの型を判定する手段として実務でも頻用されます。
-
抑えるべきポイント:Union型を扱うときは型ガードで分岐処理を明確にする
-
注意点:as に頼りすぎると型安全が損なわれるので、まずはガードから!
型変換を理解することは「型を制する」第一歩
本記事のまとめ
-
JavaScriptは「暗黙変換」に注意。明示的変換を習慣化する
-
Javaでは「キャスト+例外」に注意。instanceof を活用
-
TypeScriptは「型アサーション+ガード」で安全性を担保
-
実務では、フォーム入力、APIレスポンス、型の異なる変数操作など、型変換は日常茶飯事
-
型変換を正しく使いこなすことが、バグ防止・品質向上につながる
【外部リンク】
TypeScript Handbook – Everyday Types
Java SE 8 API – java.lang.Integer
【内部リンク】
非同期処理の基本を理解|setTimeout・Promise・async/awaitの違いと使い方