ボックス化 (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では、プリミティブ型の値でもフィールドを参照できたり、メソッドが呼び出せます。
ts
conststr = "abc";// オブジェクトのように扱うstr .length ; // フィールドの参照str .toUpperCase (); // メソッド呼び出し
ts
conststr = "abc";// オブジェクトのように扱うstr .length ; // フィールドの参照str .toUpperCase (); // メソッド呼び出し
プリミティブ型の値はオブジェクトではないため、このような操作ができるのは変です。ボックス化する必要があるように思えます。しかし、このようなことができるのは、JavaScriptが内部的にプリミティブ型の値をオブジェクトに変換しているからです。この暗黙の変換を自動ボックス化(auto-boxing)と呼びます。
ラッパーオブジェクト
JavaScriptの自動ボックス化で変換先となるオブジェクトをラッパーオブジェクト(wrapper object)と呼びます。プリミティブ型とラッパーオブジェクトの対応は次の表のとおりです。
プリミティブ型 | ラッパーオブジェクト |
---|---|
boolean | Boolean |
number | Number |
string | String |
symbol | Symbol |
bigint | BigInt |
プリミティブ型のundefined
とnull
にはラッパーオブジェクトがありません。したがって、メソッドやフィールドの参照は常にエラーが発生します。
ts
Object is possibly 'null'.2531Object is possibly 'null'.null. toString ();Object is possibly 'undefined'.2532Object is possibly 'undefined'.. undefined toString ();
ts
Object is possibly 'null'.2531Object is possibly 'null'.null. toString ();Object is possibly 'undefined'.2532Object is possibly 'undefined'.. undefined toString ();
MDNの読み方
JavaScriptを学ぶ過程で一度はお世話になるドキュメントがMDN Web Docsです。自動ボックス化とラッパーオブジェクトを意識すると、MDNのドキュメントが理解しやすくなります。
たとえば、数値のtoString
メソッドの説明は、MDNでは「Number.prototype.toString()」というタイトルのページに書かれています。toString
がプリミティブ型のnumber
に生えているものだと思っていると、「Number.prototypeは何だろう」「数値型を調べているはずなのに、なぜNumberオブジェクトのページに書いてあるんだろう」などといった疑問を持つかもしれません。
自動ボックス化とラッパーオブジェクトを知っていると、この疑問が解消します。number
にはメソッドもフィールドもありません。メソッドなどがあるように見えるのは、自動ボックス化でnumber
がNumber
オブジェクトに変換されるためです。したがって、toString
の説明がNumber
オブジェクトのページに書いてあることが腑に落ちます。また、Number.prototype
が表す意味は「Number
オブジェクトのインスタンスに生えている」ということも理解できます。
ラッパーオブジェクトとTypeScriptの型
TypeScriptでは、ラッパーオブジェクトの型も定義されています。次のように、ラッパーオブジェクトの型を使って、型注釈を書くこともできます。ラッパーオブジェクト型の変数にプリミティブ型の値を代入するのも可能です。
ts
constbool :Boolean = false;constnum :Number = 0;conststr :String = "";constsym :Symbol =Symbol ();constbig :BigInt = 10n;
ts
constbool :Boolean = false;constnum :Number = 0;conststr :String = "";constsym :Symbol =Symbol ();constbig :BigInt = 10n;
しかし、ラッパーオブジェクト型はプリミティブ型に代入できません。
ts
constn1 :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 ;
ts
constn1 :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 ;
ラッパーオブジェクト型は演算子が使えません。
ts
constnum :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
ts
constnum :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
ラッパーオブジェクト型は、そのインターフェースを満たしたオブジェクトであれば、プリミティブ型の値以外も代入できます。
ts
constboolLike = {valueOf (): boolean {return true;},};constbool :Boolean =boolLike ;
ts
constboolLike = {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』より