avoid-wrapper-types について
`string`や`number`などのプリミティブ型をオブジェクトラッパー型より使う理由を解説するTypeScript向けスキルです。型の不一致や実行時の予期しない挙動を避けるのに役立ちます。
主な機能
- 01小文字のプリミティブ型を使用
- 02ラッパー型との不一致を特定
- 03オブジェクト同一性と真偽値の注意点
- 04オートボクシングによるメソッド利用
活用シーン
- 01TypeScriptのプリミティブ値の型付け
- 02ラッパー型からの代入エラーの調査
- 03意図しないラッパーオブジェクトの確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ marius-townhouse/ effective-typescript-skills --skill avoid-wrapper-typesインストール後、Claude Code で/avoid-wrapper-typesを入力して読み込まれたか確認します。
詳細
オブジェクトラッパー型(String、Number、Boolean、Symbol、BigInt)を避ける
概要
小文字のプリミティブ型を使用し、大文字のラッパー型は決して使用しないでください。
JavaScript にはプリミティブ型(string、number、boolean)とオブジェクトラッパー型(String、Number、Boolean)があります。TypeScript には両方の型がありますが、ラッパー型はほとんど使用しないでください。
このスキルを使う場面
- プリミティブ値に型を付けるとき
- ラッパー型の不一致に関するエラーが表示されたとき
String !== stringである理由を理解するとき- プリミティブ型のメソッドを扱うとき
絶対に守るルール
ALWAYS use lowercase: string, number, boolean, symbol, bigint
NEVER use uppercase: String, Number, Boolean, Symbol, BigInt
覚えておきましょう:
- ラッパーはオブジェクトですが、プリミティブはオブジェクトではありません
- TypeScript は、ラッパー型が期待される場所でプリミティブ型を問題なく受け入れます
- TypeScript は、プリミティブ型が期待される場所でラッパー型を受け入れません
- オートボクシングにより、プリミティブ型でもメソッドを使用できます
見分け方:ラッパーの罠
// These look similar but behave differently
const primitive: string = "hello";
const wrapper: String = new String("hello");
// Equality fails
primitive === wrapper; // false (different types)
wrapper === wrapper; // Wait, this is also comparing to itself...
const s1 = new String("hello");
const s2 = new String("hello");
s1 === s2; // false! Each wrapper is a unique object
プリミティブ型のメソッドの仕組み
プリミティブ型にはメソッドがありませんが、次のコードは動作します。
'primitive'.charAt(3); // 'm' - how?
JavaScript はオートボクシングを行います。
- 一時的な
Stringラッパーを作成する - ラッパーでメソッドを呼び出す
- ラッパーを破棄する
- 結果を返す
ラッパーの欠点を伴わずに、便利にメソッドを使えます。
型の代入に関する問題
// Primitive assignable to wrapper (OK but don't do this)
const s: String = "primitive"; // Works
// Wrapper NOT assignable to primitive (Error)
function takesString(s: string) { }
takesString(new String("hello"));
// ~~~~~~~~~~~~~~~~~~~
// Argument of type 'String' is not assignable to parameter of type 'string'.
// 'string' is a primitive, but 'String' is a wrapper object.
すべてのプリミティブ型
| プリミティブ型(使用する) | ラッパー型(避ける) |
|---|---|
string | String |
number | Number |
boolean | Boolean |
symbol | Symbol |
bigint | BigInt |
注:null と undefined にはラッパー型がありません。
ラッパー型が問題になる理由
同一性の問題
const s1 = new String("hello");
const s2 = new String("hello");
s1 == s2; // false
s1 === s2; // false
// Each is a distinct object
typeof の混乱
typeof "hello"; // "string"
typeof new String("hello"); // "object"
真偽値判定の落とし穴
const falsy = new Boolean(false);
if (falsy) {
console.log("This runs!"); // Wrapper objects are always truthy
}
例外:実行時メソッド
ラッパー型の静的メソッドは使用できます。
// These are fine - not creating wrapper objects
String.fromCharCode(65); // "A"
Number.isNaN(x);
BigInt(123); // Creates primitive, not wrapper
ただし、型として使用したり、インスタンスを作成したりしてはいけません。
圧力に負けないための指針
1. 「String のメソッドを使う必要がある」
圧力:「String のメソッドを使いたいので、String 型にします」
回答: プリミティブ型の string でも、オートボクシングによって同じメソッドをすべて使えます。
対応: string 型を使用してください。メソッドはそのまま動作します。
2. 「TypeScript が String(大文字)と推論した」
圧力:「TypeScript が String と推論したのだから、それが正しいはずです」
回答: コードを確認してください。誤ってラッパーを作成している可能性があります。
対応: 原因を特定して修正してください。小文字を使用します。
危険信号 — 作業を止めて再検討してください
- 型注釈に大文字の
String、Number、Booleanがある new String()、new Number()、new Boolean()がある- ラッパー型とプリミティブ型の代入に関するエラーがある
- プリミティブ型を想定しているのに
typeof x === "object"を使用している
よくある正当化(すべて誤り)
| 言い訳 | 実際のところ |
|---|---|
| 「String のほうがメソッドが多い」 | いいえ、どちらも同じメソッドを持ちます |
| 「オブジェクトが必要だ」 | 必要になることはほとんどありません。プリミティブのほうがシンプルです |
| 「同じものだ」 | いいえ、ラッパーには同一性や真偽値判定の問題があります |
クイックリファレンス
// ALWAYS use these
const s: string = "hello";
const n: number = 42;
const b: boolean = true;
const sym: symbol = Symbol();
const big: bigint = 9007199254740991n;
// NEVER use these
const s: String = "hello"; // Wrong
const n: Number = 42; // Wrong
const b: Boolean = true; // Wrong
要点
常に小文字のプリミティブ型を使用してください。
型として String、Number、Boolean を使用する理由はありません。大文字の型は代入エラーや同一性の問題を引き起こし、小文字のプリミティブ型に勝る利点もありません。
参考資料
Dan Vanderkam 著『Effective TypeScript』の項目 10「オブジェクトラッパー型(String、Number、Boolean、Symbol、BigInt)を避ける」に基づいています。