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") // falseTypeScript での区別
TypeScript は、プリミティブとラッパーオブジェクトを別の型にすることで、この区別をモデリングしている。string は String に代入できるが、String は string に代入できない。そのため、次の関数は型エラーになる。こうした違いを考えるのは面倒なので、ラッパーオブジェクト型は使わないほうがよい。
const isGreeting = (phrase: String) => ["hello", "good day"].includes(phrase)大文字の型で型アノテーションを付けた場合も、型はラッパーオブジェクトになる。ただし実行時の値はプリミティブのままである(項目3)。このような型アノテーションは冗長で、読みやすさも下げるので避ける。
const s: String = "primitive" // string は String に代入できるので OKtypescript-eslint の no-wrapper-object-types ルールを有効にすると、ラッパーオブジェクト型の利用を禁止できる。このルールは、recommended の設定で有効になる。
BigInt と Symbol
BigInt と Symbol を new なしで呼び出すと、プリミティブ値が生成されるので問題ない。なお、bigint の値は、123n のように数値の末尾に n を付けても作れる。
typeof BigInt(123) // 'bigint'
typeof Symbol("sym") // 'symbol'キーワード
- ラッパーオブジェクト
- 型アノテーション
- typescript-eslint
- ESLint