TypedArrayの型判定:なぜ `typeof` や `instanceof` だけでは「現場」で戦えないのか
フロントエンドの現場で、バイナリデータを扱う機会が増えてきたと感じることはないだろうか? WebAssemblyとの連携、Canvasのピクセル操作、あるいはWebSocket経由の高速なデータ転送。そんな時、避けて通れないのが `Uint8Array` や `Float32Array` といった「TypedArray」の存在だ。
中級エンジニアの君なら、一度は「このデータ、本当にTypedArrayか?」と判定するコードを書いたことがあるはずだ。だが、安易に `typeof` を使って痛い目を見たことはないだろうか?
今日は、JavaScriptの仕様の裏側を覗きながら、現場で「絶対にバグらせない」TypedArrayの判定術を伝授しよう。
—
1. なぜ `typeof` は役に立たないのか
まず結論から言う。TypedArrayに対して `typeof` を使ってはいけない。
const buffer = new Uint8Array([1, 2, 3]);
console.log(typeof buffer); // -> “object”
そう、JavaScriptにおいてTypedArrayは単なる「オブジェクト」だ。これでは `[]`(配列)や `{}`(プレーンオブジェクト)と区別がつかない。次に頭に浮かぶのが `instanceof` だろう。しかし、これも落とし穴がある。
2. `instanceof` の盲点とクロスフレームワークの罠
`instanceof` はプロトタイプチェーンを辿る。基本的にはこれで動く。
console.log(buffer instanceof Uint8Array); // -> true
しかし、現場で恐ろしいのは「複数の実行環境(iframeやウィンドウ)」を跨ぐ場合だ。iframe内で生成された `Uint8Array` を親ウィンドウに渡すと、プロトタイプが異なるため `instanceof` は無慈悲にも `false` を返す。これに気づかずリリースして、デバッグで冷や汗をかいた経験がある人は多いはずだ。
—
3. 最強の判定術:`Object.prototype.toString` の活用
そこで我々プロフェッショナルが頼るのは、`Object.prototype.toString.call()` によるクラス名判定だ。これは内部スロット `[[Prototype]]` に依存せず、エンジンが管理する内部的な型情報を直接引き抜く手法だ。
TypedArrayを一括で判定するスマートな関数
特定の型(Uint8とか)を特定したい場合と、とにかく「TypedArrayのどれかであること」を確認したい場合があるだろう。実務で使えるコードがこれだ。
/
- 渡された値がTypedArrayのいずれかであるかを判定する
/
function isTypedArray(value) {
// toString.call(value) は “[object Uint8Array]” などを返す
const typeString = Object.prototype.toString.call(value);
// TypedArrayは全て “[object …Array]” という形式で終わる
// 念のため、nullやundefined以外のオブジェクトであることを確認する
return (
value !== null &&
typeof value === ‘object’ &&
typeString.endsWith(‘Array]’) &&
typeString !== ‘[object Array]’ // 普通の配列(Array)を除外
);
}
// 使い方
console.log(isTypedArray(new Uint8Array([1]))); // -> true
console.log(isTypedArray(new Float32Array([1]))); // -> true
console.log(isTypedArray([1, 2, 3])); // -> false (普通の配列)
console.log(isTypedArray({})); // -> false
4. ブラウザが裏側でやっていること
少しだけエンジンの話をしよう。JavaScriptエンジン(V8など)は、TypedArrayを通常のオブジェクトとは全く異なるメモリ構造で管理している。
通常の配列は「穴」が開くこともあるし、どんな型でも入る「動的な箱」だが、TypedArrayは「固定されたメモリバッファ(ArrayBuffer)へのビュー(覗き窓)」に過ぎない。エンジンは、このバッファにアクセスする際、型に基づいたオフセット計算をハードウェアレベルで最適化している。
`Object.prototype.toString` が正確なのは、この「型付けされたビュー」であることをエンジンが内部的にタグとして保持しているからだ。このタグを拝借するのが、最も低レイヤーで堅牢な手段というわけだ。
—
実務のアドバイス:結局どう書くのがベストか?
もし君がTypeScriptを使っているなら、型ガード(Type Guard)と組み合わせるのが最強だ。
function isTypedArray(value: unknown): value is TypedArray {
return ArrayBuffer.isView(value) && !(value instanceof DataView);
}
実は、`ArrayBuffer.isView()` という非常に便利なメソッドがある。これは `TypedArray` か `DataView` を判定するものだ。`DataView` はTypedArrayではないので、そこだけ弾いてやれば、驚くほどスッキリしたコードになる。
- 単一の型を判定したい時: `value instanceof Uint8Array` (シンプルさ優先)
- 汎用的なバリデーション: `ArrayBuffer.isView(value) && !DataView` (堅牢さ優先)
最後に
JavaScriptの型判定は、一見すると「どれを使っても同じじゃないか」と思われがちだ。だが、それは「動くコード」を書いているだけ。今回紹介したような、仕様の裏側を理解した上での「壊れないコード」を書く姿勢こそが、シニアエンジニアへの第一歩だ。
明日からのコードレビューで、`typeof` で判定している箇所を見かけたら、ニヤリと笑って今回のTipsを教えてやってくれ。現場からは以上だ。

コメント