【入門編】プリミティブ型における「小文字」と「大文字」の非対称性:なぜStringを使ってはいけないのか – TypeScript コア・型システムの基礎解析バイブル

こんにちは!TypeScriptの世界へようこそ。
日々、フロントエンドからバックエンドまでバリバリとコードを書いていると、ふと「あれ?」と立ち止まる瞬間に出会いませんか?

今回は、TypeScriptを学び始めた方や、他のプログラメント言語(JavaやC#など)からやってきた開発者が、99%の確率で一度は踏み込んでしまう「落とし穴」についてお話しします。

そのテーマは、プリミティブ型における「小文字」と「大文字」の非対称性:なぜ `String` を使ってはいけないのか です。

「`string` と `String` って、大文字か小文字かの違いだけじゃないの?」
そう思ったそこのあなた。ここをクリアすれば、TypeScriptの型システムの基本はバッチリマスターできますよ!
さあ、コンパイラの裏側で何が起きているのか、一緒に紐解いていきましょう。

—

1. ざっくり言うと何が違うの?(イメージ図解)

まずは、JavaScriptおよびTypeScriptにおける両者の正体を、視覚的なイメージで掴んでみましょう。

  • `string` (小文字) = 「純粋なデータそのもの(値)」
  • 例:`”Hello, TypeScript”` という文字の塊そのもの。軽くて素朴で、余計な機能を持っていません。
  • `String` (大文字) = 「データに色々なお世話機能がついた『箱(オブジェクト)』」
  • 例:文字を包み込む「豪華なギフトボックス」。中身を取り出すこともできますが、箱自体はただの文字ではありません。

TypeScriptの型システムにおいて、この「中身」と「箱」は全く異なる宇宙に存在しています。

—

2. コードで見る挙動の違い

百聞は一見にしかず。実際のコードで、この非対称性がどう現れるのかを見てみましょう。

// 1. 小文字の string 型(プリミティブ型)
const userName: string = “Taro”;

// 2. 大文字の String 型(オブジェクトラッパー型)
// ※コンパイルエラーにはなりませんが、TypeScriptでは強く非推奨とされます
const userBox: String = “Taro”;

ここまでは「動くじゃん」と思いますよね。問題は、これを関数の引数や別の変数に代入する時に牙をむきます。

function printGreeting(text: string) {
console.log(`こんにちは、${text}さん!`);
}

// ── 正常なパターン ──
const rawString = “Hanako”;
printGreeting(rawString); // OK! string を渡しているので完璧です。

// ── 危険なパターン ──
const boxedString: String = new String(“Jiro”);
// printGreeting(boxedString);
// 🔴 ここで盛大なコンパイルエラーが発生します!
// 「型 ‘String’ の引数を型 ‘string’ のパラメータに割り当てることはできません。
// ‘string’ はプリミティブですが、’String’ はラッパーです。可能な限り ‘string’ を使用してください。」

TypeScriptのコンパイラは、慈悲深い警告を出してくれます。
「おいおい、`printGreeting` が求めているのは中身の『文字(string)』なのに、君が渡したのは『Stringという名のギフトボックス』じゃないか!」と怒られているわけです。

—

3. なぜ `String`(大文字)を使ってはいけないのか?(型システムの深層)

では、なぜTypeScript(およびJavaScript)のコアコミッターたちは、大文字の `String` を使うことを「絶対にやめてほしい」と考えているのでしょうか。理由は大きく3つあります。

① 型のミスマッチ(プリミティブとオブジェクトの乖離)

JavaScriptの歴史的背景として、`new String(“hello”)` のようなコードを書くと、それは「オブジェクト」になります。
typeof 演算子を使って確かめてみると、その恐ろしさが分かります。

const a: string = “hello”;
console.log(typeof a); // “string” (プリミティブ)

const b: String = new String(“hello”);
console.log(typeof b); // “object” (オブジェクト!)

`string` はデータそのものですが、`String` はメソッドやプロパティを持った参照型のオブジェクトです。TypeScriptの型安全性の基本は「データ構造と型が一致していること」なので、プリミティブを期待する場所にオブジェクトを持ち込むのは、型システムの意図を真っ向から破壊する行為になります。

② 思わぬバグを生む「比較」の罠

オブジェクト同士を比較するとき、JavaScriptでは「中身が同じか」ではなく「メモリ上の同じ場所を指しているか」が比較されます。

const str1: String = new String(“hello”);
const str2: String = new String(“hello”);

console.log(str1 === str2);
// ❌ 実行結果はなんと `false` になります!
// 中身は同じ “hello” なのに、別の箱(オブジェクト)だからです。

これ、バグとして混入したら原因究明に何時間も溶かす悪夢の挙動ですよね。小文字の `string`(プリミティブ)であれば、値の比較は常に `===` で直感通りに正しく行われます。

③ ボトリング(無駄なメモリ消費)

大文字の `String` オブジェクトを作るたびに、JavaScriptエンジンはメモリ上に余計なオブジェクトの枠組みを生成します。プリミティブな `string` はメモリ効率が極限まで最適化されているため、パフォーマンスの観点からも圧倒的に有利です。

—

4. 他のプリミティブ型でも同じルールですか?

はい、すべて同じです!
TypeScript(JavaScript)におけるプリミティブ三兄弟は、すべてこの「小文字と大文字の非対称性」を持っています。

| 型分類 | 正しい書き方(プリミティブ) | 絶対に避けるべき書き方(ラッパーオブジェクト) |
| :— | :— | :— |
| 文字列 | `string` | `String` |
| 数値 | `number` | `Number` |
| 真偽値 | `boolean` | `Boolean` |

例えば、型定義を書くときは常に小文字を使うと覚えておいてください。

// ⭕️ 正しい型定義の例
let isReady: boolean = true;
let count: number42 = 42; // あ、42は値ですね let count: number = 42;
let message: string = “TypeScript最高!”;

—

まとめ:今日の極意

  • 小文字(`string`, `number`, `boolean`)を使おう
  • これらはデータそのものを表すプリミティブ型であり、TypeScriptの型システムが最も美しく、安全に機能します。
  • 大文字(`String`, `Number`, `Boolean`)は型としては絶対に使わない
  • これらはJavaScriptの歴史的遺産であるオブジェクトラッパーであり、予期せぬバグや型エラーの原因になります。

「型を書くときは、迷ったら小文字!」
この原則さえ頭に置いておけば、もう型の世界で迷子になることはありません。

ここをクリアしたあなたなら、TypeScriptの基本はもうバッチリマスターできていますよ。自信を持って次のステップへ進んでいきましょう!
それでは、快適なTypeScriptライフを!

タイトルとURLをコピーしました