TL;DR

  • プリミティブ型のラッパーオブジェクトは使わない。ラッパーオブジェクトは、プリミティブ値にメソッドを提供したり、静的メソッドを提供したりするために存在している。
  • ただし、Symbol と BigInt を new なしで呼び出すとプリミティブ値が生成されるので、問題ない。

内容

プリミティブとラッパーオブジェクト

JavaScript は、null と undefined 以外のプリミティブ値を、必要に応じてラッパーオブジェクトに自動で変換する。プリミティブ型 string、number、boolean、symbol、bigint に対応するラッパーオブジェクトが、String、Number、Boolean、Symbol、BigInt である。ラッパーオブジェクトはメソッドや静的メソッドを提供するために存在しているので、直接インスタンス化する理由はない。

自動で変換されることは、String.prototype をモンキーパッチして確かめられる。

const originalCharAt = String.prototype.charAt
String.prototype.charAt = function (pos) {
  console.log(this, typeof this, pos)
  return originalCharAt.call(this, pos)
}
 
"primitive".charAt(3)
// [String: 'primitive'] object 3

ラッパーオブジェクトはオブジェクトなので、自分自身としか等しくならない。

"hello" === new String("hello") // false
new String("hello") === new String("hello") // false

TypeScript での区別

TypeScript は、プリミティブとラッパーオブジェクトを別の型にすることで、この区別をモデリングしている。string は String に代入できるが、String は string に代入できない。そのため、次の関数は型エラーになる。こうした違いを考えるのは面倒なので、ラッパーオブジェクト型は使わないほうがよい。

const isGreeting = (phrase: String) => ["hello", "good day"].includes(phrase)

大文字の型で型アノテーションを付けた場合も、型はラッパーオブジェクトになる。ただし実行時の値はプリミティブのままである(項目3)。このような型アノテーションは冗長で、読みやすさも下げるので避ける。

const s: String = "primitive" // string は String に代入できるので OK

typescript-eslint の no-wrapper-object-types ルールを有効にすると、ラッパーオブジェクト型の利用を禁止できる。このルールは、recommended の設定で有効になる。

BigInt と Symbol

BigInt と Symbol を new なしで呼び出すと、プリミティブ値が生成されるので問題ない。なお、bigint の値は、123n のように数値の末尾に n を付けても作れる。

typeof BigInt(123) // 'bigint'
typeof Symbol("sym") // 'symbol'

キーワード