【入門編】プリミティブ型ラッパー(String, Number等)の罠:なぜ小文字を使うべきなのか – TypeScript コア・型システムの基礎解析バイブル

こんにちは!TypeScriptの世界へようこそ。

あなたがこれまでJavaやC#、あるいはJavaScriptを触ってきた中で、「型を定義するとき、`string`と書くべきか`String`と書くべきか?」と一瞬迷ったことはありませんか?

「どっちでも動くし、似たようなものだろう」……もしそう思っていたら、そこにはTypeScriptの型システムが仕掛けた巧妙な罠が潜んでいます。

今日は、TypeScriptのコアを支える「プリミティブ型」と「オブジェクトラッパー型」の決定的な違いについて、コンパイラの裏側で何が起きているのかを交えながら、優しく、かつ深く解説していきます。ここをクリアすれば、あなたのTypeScriptの理解度は一段上のステージへと進みますよ。

—

1. 似て非なるもの:`string` と `String`

まず、結論からお伝えします。
TypeScriptでの型定義には、常に小文字の `string`, `number`, `boolean` を使ってください。

なぜ大文字(`String`, `Number`)を使ってはいけないのか。それを理解するために、まずはこの2つの正体を見てみましょう。

// 1. プリミティブ型 (推奨)
let name: string = “TypeScript”;

// 2. オブジェクトラッパー型 (非推奨)
let nameObject: String = new String(“TypeScript”);

プリミティブ型 (`string`)

これは「値そのもの」です。メモリ上に直接置かれる軽量なデータであり、JavaScriptの実行エンジンが最も効率よく扱える形式です。

オブジェクトラッパー型 (`String`)

これは「値を包み込んだオブジェクト」です。JavaScriptの歴史的な経緯から存在するもので、`new String()` とすることで、文字列を扱うためのメソッドを内包した「重厚なオブジェクト」が生成されます。

—

2. なぜ「小文字」が正解なのか?

「大文字の方が高機能そうだし、大は小を兼ねるのでは?」と思うかもしれません。しかし、TypeScriptの型システムにおいては、その逆が起きます。

以下のコードを、コンパイラの気持ちになって見てみましょう。

function greet(message: string) {
console.log(message);
}

const primitiveStr: string = “hello”;
const objectStr: String = new String(“hello”);

greet(primitiveStr); // OK!
greet(objectStr); // Error: ‘String’ 型の引数を ‘string’ 型のパラメーターに割り当てることはできません。

ここでエラーが出る理由、わかりますか?
実は、「`String`(大文字)は `string`(小文字)ではない」 からです。

互換性の一方通行

TypeScriptでは、以下のようなルールがあります。

  • `string`(小文字)の値は、`String`(大文字)の型に代入できる。
  • `String`(大文字)の値は、`string`(小文字)の型に代入できない。

これは、`string`(プリミティブ)が「純粋なデータ」であるのに対し、`String`(オブジェクト)は「余計なプロパティをたくさん持った複雑な構造体」だからです。

—

3. JavaScriptの魔法「オートボクシング」を知る

「でも、小文字の `string` を使っていても、`.length` や `.toUpperCase()` などのメソッドが呼べるのはなぜ?」

鋭い質問ですね!ここにJavaScriptの面白い仕組みがあります。これが 「オートボクシング(自動箱詰め)」 です。

const text: string = “hello”;

// 実行時、一時的に “hello” は Stringオブジェクトに包まれ(ボクシング)、
// メソッドを実行した直後に、またプリミティブに戻ります。
console.log(text.toUpperCase());

TypeScriptはこの「一時的にオブジェクトとして振る舞う」というJavaScriptの挙動を完璧に理解しています。そのため、わざわざ自分から大文字の `String` を使って「ずっと重いオブジェクトのまま」にする必要はないのです。

むしろ、常に大文字の型を使っていると、`typeof` 演算子を使った時に予期せぬ挙動に悩まされることになります。

const s1: string = “test”;
const s2: String = new String(“test”);

console.log(typeof s1); // “string”
console.log(typeof s2); // “object” ← これがバグの原因になりやすい!

—

4. コンパイラAPIから見た「型の重み」

少しだけ専門的な話をすると、TypeScriptの内部(`lib.d.ts`)では、`String` はインターフェースとして定義されています。

// イメージ的な内部定義
interface String {
toString(): string;
charAt(pos: number): string;
length: number;
// …その他多くのメソッド
}

あなたが `string`(小文字)を使うとき、TypeScriptは「あ、これは軽量な基本データだね」と認識し、必要な時だけこのインターフェースを参照します。
しかし、あなたが `String`(大文字)を型として指定すると、TypeScriptは「これは特定の構造を持つオブジェクトだ」と厳格に解釈します。

この「厳格さ」が、モダンな開発においては逆にノイズとなり、「プリミティブな値を期待している場所に、オブジェクトが紛れ込む」という、JavaScript特有の厄介なバグを誘発する入り口になってしまうのです。

—

5. まとめ:これだけは覚えておきましょう

TypeScriptを掌握するための第一歩は、「言語が提供する最もシンプルで軽量な形を愛すること」です。

1. 基本は小文字!: 型定義には `string`, `number`, `boolean`, `symbol`, `bigint` を使いましょう。
2. 大文字は「値」として使う: `Number.MAX_SAFE_INTEGER` や `String.fromCharCode()` のように、型ではなく「便利な機能を持った道具箱(クラス)」として使うのが正しい姿です。
3. 他の言語の癖を捨てる: JavaやC#の `String` クラスの感覚を持ち込むと、TypeScriptでは型エラーに足元をすくわれます。

// ✅ 良い例:これぞTypeScript
const score: number = 100;
const userName: string = “Alice”;

// ❌ 避けるべき例:混乱の元
const scoreObj: Number = 100; // 代入はできるが、使おうとすると不便
const userNameObj: String = new String(“Alice”);

いかがでしたか?「小文字を使う」という単純なルール一つにも、これだけの深い理由と、JavaScriptの歴史、そしてTypeScriptの設計思想が詰まっているのです。

ここを意識するだけで、あなたの書くコードはより「TypeScriptらしく」なり、無駄な型エラーに悩まされることもなくなります。一歩ずつ、着実に掌握していきましょう。

もし何か分からないことがあれば、いつでも聞いてくださいね。あなたの素晴らしいエンジニアライフを応援しています!

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