TypeScriptでプログラミングを始める際に、文字列の型として「string」と「String」のどちらを使うべきか迷う場面は少なくありません。

一見すると大文字と小文字の違いだけに思えますが、これらはプログラミング言語としての設計上の役割が根本的に異なります。

結論から申し上げますと、TypeScriptの開発において日常的に使用すべきなのは小文字の「string」型です。

この記事では、なぜ「string」を使うべきなのか、そして「String」とは一体何なのかについて、プリミティブ型とラッパーオブジェクトという観点から詳しく解説します。

プリミティブ型とラッパーオブジェクトの基本概念

TypeScriptおよびそのベースとなっているJavaScriptには、「プリミティブ型」と「ラッパーオブジェクト」という2つの概念が存在します。

この違いを理解することが、string と String の使い分けを理解するための第一歩となります。

string (プリミティブ型) の特徴

小文字で始まる string は、TypeScriptにおけるプリミティブ型を指します。

プリミティブ型とは、メソッドを持たない単純な値のデータ型のことです。

JavaScriptには string の他に、number、boolean、null、undefined、symbol、bigint の計7つのプリミティブ型があります。

これらは非常に軽量で、メモリ効率が良く、データのコピーも値そのものが行われるという特徴があります。

String (ラッパーオブジェクト) の特徴

大文字で始まる String は、プリミティブな文字列を包み込む(ラップする)ためのラッパーオブジェクトを指します。

これはJavaScriptの標準ビルトインオブジェクトの一つであり、文字列操作のためのメソッドやプロパティを提供するための「クラス」のような存在です。

通常、プリミティブ型の文字列に対して .length や .toUpperCase() といったメソッドを呼び出す際、JavaScriptは内部的にこの String オブジェクトを一時的に生成します。

この仕組みは「オートボクシング (自動箱詰め)」と呼ばれ、開発者が意識することなくプリミティブ型をオブジェクトのように扱えるようにしています。

stringとStringの決定的な違いを比較

次に、具体的なコード例を交えながら、これら2つの型がどのように異なるのかを比較してみましょう。

型の互換性と代入ルール

TypeScriptにおいて、string と String は型としての互換性が完全ではありません。

以下のコードを見て、代入の可否を確認してください。

TypeScript
// プリミティブ型の変数
let primitiveStr: string = "hello";

// ラッパーオブジェクト型の変数
let wrapperStr: String = "world";

// 1. string型にString型を代入しようとする (エラー)
// primitiveStr = wrapperStr; // Error: 'String' is not assignable to type 'string'.

// 2. String型にstring型を代入しようとする (成功)
wrapperStr = primitiveStr; // OK

このように、「大文字の String 型」には「小文字の string 型」を代入できますが、その逆は許可されません。

これは、String オブジェクトが string プリミティブが必要とする情報をすべて持っている一方で、プリミティブ型はオブジェクト特有のプロパティを保持できないためです。

インスタンス化とtypeof演算子の挙動

new キーワードを使用して String オブジェクトを明示的に作成した場合、その型は明確に object と判定されます。

TypeScript
const typeA: string = "hello";
const typeB: String = new String("hello");

console.log(typeof typeA);
console.log(typeof typeB);
実行結果
"string"
"object"

実行結果から分かる通り、new String() で生成された値は、文字列データを含んだオブジェクトです。

一方、リテラルで記述した文字列は純粋な string 型です。

等価演算子による比較の罠

ラッパーオブジェクトを使用すると、値の比較において予期しない挙動が発生することがあります。

TypeScript
const s1 = "test";
const s2 = new String("test");

console.log(s1 == s2);  // 値の比較なので true
console.log(s1 === s2); // 型が異なるため false
実行結果
true
false

厳密等価演算子 (===) を使用した場合、string と String は別物として扱われるため、バグの原因になりやすいです。

開発現場で string を選ぶべき理由

現代のTypeScript開発において、型定義に String を使うことはほぼありません。

その理由を、パフォーマンスと設計の観点から解説します。

TypeScriptの設計思想とベストプラクティス

TypeScriptの公式ドキュメントや有名なスタイルガイドでは、「プリミティブ型には小文字の型名を使用すること」が強く推奨されています。

これは string に限らず、number や boolean においても同様です。

大文字の型名は「JavaScriptのグローバルオブジェクト」としての型を指すため、意図しないオブジェクトの混入を許してしまい、型の安全性が低下します。

メモリ効率と実行速度の観点

プリミティブ型の string は、スタック領域などで効率的に管理されます。

これに対し String オブジェクトはヒープ領域にメモリを確保し、オブジェクトとしてのオーバーヘッドが発生します。

大量の文字列データを扱うアプリケーションにおいて、不必要に String オブジェクトを作成することはリソースの無駄遣いに繋がります。

ESLintによるString型の使用制限

多くのプロジェクトで導入されている ESLint のデフォルトルール設定 (特に @typescript-eslint/recommended) では、大文字の String 型の使用は警告またはエラーとして扱われます。

これは、開発者が誤ってラッパーオブジェクト型を指定してしまうミスを防ぐための標準的な措置です。

特徴string (推奨)String (非推奨)
種類プリミティブ型ラッパーオブジェクト
typeof の結果“string”“object”
メモリ消費低い (効率的)高い (オーバーヘッドあり)
主な用途変数、引数、戻り値の型定義静的メソッドの利用 (稀)

Stringラッパーオブジェクトが必要になる特殊なケース

「String」は型定義としては使いませんが、オブジェクトとしての String 自体は非常に有用な機能を備えています。

どのような場面で大文字の String が現れるのかを確認しておきましょう。

Stringクラスが持つ静的メソッドの利用

String オブジェクトには、文字列の生成や操作に役立つ静的メソッドがいくつか用意されています。

例えば、Unicodeのコードポイントから文字列を作成する fromCharCode などが代表的です。

TypeScript
// Unicodeコードポイントから文字列を作成
const greeting = String.fromCharCode(72, 101, 108, 108, 111);

console.log(greeting);
実行結果
"Hello"

このように、特定の便利な機能にアクセスするために String という名前を使用することは正当なプログラミング手法です。

ただし、この場合でもメソッドの戻り値はプリミティブ型の string になります。

明示的な型変換としての String()

他のデータ型を文字列に変換したい場合、コンストラクタ関数として String() を呼び出すことがあります。

TypeScript
const num = 123;
const strFromNum = String(num); // "123" という string 型が返る

console.log(typeof strFromNum);
実行結果
"string"

ここで重要なのは、new を付けずに String() を関数として呼び出した場合、戻り値は「ラッパーオブジェクト」ではなく「プリミティブ型の string」になるという点です。

これは非常に安全かつ一般的な型変換の方法です。

まとめ

TypeScriptにおける string と String の違いについて解説してきました。

型定義や変数の宣言においては、常に小文字の「string」を使用してください。

大文字の String は JavaScript の内部的なラッパーオブジェクトを指しており、通常の開発で型として使用するメリットはありません。

むしろ、厳密な比較 (===) で意図しない結果を招いたり、メモリ効率を低下させたりといったデメリットが目立ちます。

「型は小文字、便利な機能を使いたい時は大文字のオブジェクトを介する」というルールを徹底することで、より安全でクリーンな TypeScript コードを記述できるようになります。

もし既存のコードで String 型が使われている箇所を見つけた場合は、特別な理由がない限り string 型へのリファクタリングを検討することをお勧めします。