ボックス化 (boxing)
多くの言語では、プリミティブは一般的にフィールドやメソッドを持ちません。プリミティブをオブジェクトのように扱うには、プリミティブをオブジェクトに変換する必要があります。プリミティブからオブジェクトへの変換をボックス化(boxing)と言います。
ts// プリミティブ型conststr = "abc";// ラッパーオブジェクトに入れるconststrObject = newString (str );// オブジェクトのように扱うstrObject .length ; // フィールドの参照strObject .toUpperCase (); // メソッド呼び出し
ts// プリミティブ型conststr = "abc";// ラッパーオブジェクトに入れるconststrObject = newString (str );// オブジェクトのように扱うstrObject .length ; // フィールドの参照strObject .toUpperCase (); // メソッド呼び出し
上の例は、JavaScriptでボックス化のイメージを書いたものです。実際のコードでは、プリミティブ型をStringのようなラッパーオブジェクトにわざわざ入れる必要はありません。JavaScriptには自動ボックス化という仕組みがあるからです。
自動ボックス化
JavaScriptでは、プリミティブ型の値でもフィールドを参照できたり、メソッドが呼び出せます。
tsconststr = "abc";// オブジェクトのように扱うstr .length ; // フィールドの参照str .toUpperCase (); // メソッド呼び出し
tsconststr = "abc";// オブジェクトのように扱うstr .length ; // フィールドの参照str .toUpperCase (); // メソッド呼び出し
プリミティブ型の値はオブジェクトではないため、このような操作ができるのは変です。ボックス化する必要があるように思えます。しかし、このようなことができるのは、JavaScriptが内部的にプリミティブ型の値をオブジェクトに変換しているからです。この暗黙の変換を自動ボックス化(auto-boxing)と呼びます。
ラッパーオブジェクト
JavaScriptの自動ボックス化で変換先となるオブジェクトをラッパーオブジェクト(wrapper object)と呼びます。プリミティブ型とラッパーオブジェクトの対応は次の表のとおりです。
| プリミティブ型 | ラッパーオブジェクト |
|---|---|
boolean | Boolean |
number | Number |
string | String |
symbol | Symbol |
bigint | BigInt |
プリミティブ型のundefinedとnullにはラッパーオブジェクトがありません。したがって、メソッドやフィールドの参照は常にエラーが発生します。
tsThe value 'null' cannot be used here.18050The value 'null' cannot be used here.null. toString ();The value 'undefined' cannot be used here.18050The value 'undefined' cannot be used here.. undefined toString ();
tsThe value 'null' cannot be used here.18050The value 'null' cannot be used here.null. toString ();The value 'undefined' cannot be used here.18050The value 'undefined' cannot be used here.. undefined toString ();
MDNの読み方
JavaScriptを学ぶ過程で一度はお世話になるドキュメントがMDN Web Docsです。自動ボックス化とラッパーオブジェクトを意識すると、MDNのドキュメントが理解しやすくなります。
たとえば、数値のtoStringメソッドの説明は、MDNでは「Number.prototype.toString()」というタイトルのページに書かれています。toStringがプリミティブ型のnumberに生えているものだと思っていると、「Number.prototypeは何だろう」「number型を調べているはずなのに、なぜNumberオブジェクトのページに書いてあるんだろう」などといった疑問を持つかもしれません。
自動ボックス化とラッパーオブジェクトを知っていると、この疑問が解消します。numberにはメソッドもフィールドもありません。メソッドなどがあるように見えるのは、自動ボックス化でnumberがNumberオブジェクトに変換されるためです。したがって、toStringの説明がNumberオブジェクトのページに書いてあることが腑に落ちます。また、Number.prototypeが表す意味は「Numberオブジェクトのインスタンスに生えている」ということも理解できます。
ラッパーオブジェクトとTypeScriptの型
TypeScriptでは、ラッパーオブジェクトの型も定義されています。次のように、ラッパーオブジェクトの型を使って、型注釈を書くこともできます。ラッパーオブジェクト型の変数にプリミティブ型の値を代入するのも可能です。
tsconstbool :Boolean = false;constnum :Number = 0;conststr :String = "";constsym :Symbol =Symbol ();constbig :BigInt = 10n;
tsconstbool :Boolean = false;constnum :Number = 0;conststr :String = "";constsym :Symbol =Symbol ();constbig :BigInt = 10n;
しかし、ラッパーオブジェクト型はプリミティブ型に代入できません。
tsconstn1 :Number = 0;constType 'Number' is not assignable to type 'number'. 'number' is a primitive, but 'Number' is a wrapper object. Prefer using 'number' when possible.2322Type 'Number' is not assignable to type 'number'. 'number' is a primitive, but 'Number' is a wrapper object. Prefer using 'number' when possible.: number = n2 n1 ;
tsconstn1 :Number = 0;constType 'Number' is not assignable to type 'number'. 'number' is a primitive, but 'Number' is a wrapper object. Prefer using 'number' when possible.2322Type 'Number' is not assignable to type 'number'. 'number' is a primitive, but 'Number' is a wrapper object. Prefer using 'number' when possible.: number = n2 n1 ;
ラッパーオブジェクト型は演算子が使えません。
tsconstnum :Number = 1;The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type.2362The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type.* 2; num
tsconstnum :Number = 1;The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type.2362The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type.* 2; num
ラッパーオブジェクト型は、そのインターフェースを満たしたオブジェクトであれば、プリミティブ型の値以外も代入できます。
tsconstboolLike = {valueOf (): boolean {return true;},};constbool :Boolean =boolLike ;
tsconstboolLike = {valueOf (): boolean {return true;},};constbool :Boolean =boolLike ;
プリミティブ型の代わりに、ラッパーオブジェクト型を型注釈に使う利点はありません。型注釈にはプリミティブ型を使いましょう。
ts// ❌間違いconstnum1 :Number = 0;// ✅正しいconstnum2 : number = 0;
ts// ❌間違いconstnum1 :Number = 0;// ✅正しいconstnum2 : number = 0;
学びをシェアする
・ボックス化とはプリミティブをオブジェクトに変換すること
・JavaScriptでプリミティブがオブジェクトのように扱えるのは、自動ボックス化のおかげ
・TypeScriptではラッパーオブジェクト(例:String)よりもプリミティブ型(例:string)で型注釈すべし
『サバイバルTypeScript』より
関連情報
📄️ プリミティブ型
JavaScriptのデータ型は、プリミティブ型とオブジェクトの2つに分類されます。