【入門編】Interfaceの「プライベートプロパティ」問題と、#記号による真のプライベート化の比較 – TypeScript コア・型システムの基礎解析バイブル

TypeScriptの「隠蔽」を極める:Interfaceの private と JavaScriptの #プライベート の境界線

こんにちは。TypeScriptの深淵を歩む皆さん、今日も型定義と格闘していますか?

TypeScriptを使っていると、「このプロパティは外部から触らせたくないな」という場面に出くわしますよね。その際、`private`修飾子を使うべきか、それともJavaScript標準の `#` を使うべきか、迷ったことはありませんか?

実は、この2つは「型レベルの制約」なのか、それとも「実行時の物理的な遮断」なのかという、決定的な違いがあります。今日はその本質を紐解いていきましょう。

—

1. そもそも TypeScriptの `private` は「型」の話でしかない

まず結論から言います。TypeScriptの `private` は、コンパイル後のJavaScriptには一切残りません。

class User {
private secret = “12345”;
}

const user = new User();
// コンパイルすると、ただのプロパティとしてJS上に存在します
console.log((user as any).secret); // “12345” が出力されてしまう!

TypeScriptの `private` は、開発中の「開発者に対する警告」です。IDE上で「そこに触っちゃダメだよ」と教えてくれる親切なガイドであって、実行時のセキュリティを保証するものではないということを、まずは心に刻んでください。

Interfaceで private は定義できる?

ここでよくある疑問が「Interfaceで `private` を指定できるか?」です。

答えは No です。
Interfaceは「公開されている契約(APIの顔)」です。`private` は「隠蔽」を意味するため、そもそも契約書であるInterfaceに `private` を書くという概念自体が矛盾しています。Interfaceはあくまで「外から見て何ができるか」を定義する場所だからです。

—

2. JavaScriptの真のプライベート「#」の登場

そこで登場するのが、ES2022から標準仕様となった `#` プレフィックスです。

class SecureUser {
#secret = “12345”; // ここに注目

getSecret() {
return this.#secret;
}
}

const user = new SecureUser();
// console.log(user.#secret); // コンパイルエラー!
// console.log((user as any).#secret); // 実行時エラー!

こちらは TypeScript の型チェックを通り抜けたとしても、実行時にJavaScriptエンジンがアクセスを物理的にブロックします。 「名前を隠す」のではなく「メモリ上の場所を隠す」というイメージです。

ここで知っておくべき「Interfaceとの相性」

ここが重要なポイントです。`#` プロパティは、Interfaceで定義できません。

interface UserInterface {
// #secret: string; // これは文法エラー!
}

なぜなら、Interfaceは「公開される外部仕様」であり、`#` は「クラス内部の隠し金庫」だからです。`#` はクラスの外からは存在すら確認できないため、Interfaceという「公開インターフェース」の設計図には到底含められないのです。

—

3. どっちを使うべき? 使い分けの黄金律

現場で迷ったときは、この基準で選んでください。

TypeScriptの `private` (または `protected`) を使うべきとき

  • チーム開発の安全性を高めたいとき: メンバーが誤って内部メソッドを呼び出さないように、IDEで警告を出したい。
  • 継承関係を使いたいとき: `protected` を使えば、サブクラスには許可しつつ、外部には隠すといった柔軟な設計が可能です。
  • Interfaceで抽象化したいとき: 「型」としての規約を重視するならこちらです。

JavaScriptの `#` を使うべきとき

  • カプセル化を厳密にしたいとき: 外部のJSコードや、悪意のある実行時アクセスから完全にデータを隠蔽したい場合。
  • ライブラリ開発など: 利用者に「クラスの内部構造を絶対に触らせない」という強い意思表示をしたいとき。

—

まとめ: TypeScriptを掌握する視点

  • TypeScriptの `private`: 開発者のための「交通ルール」。コンパイル後に消える。
  • JavaScriptの `#`: 実行時の「物理的な鍵」。Interfaceには書けない。

「Interfaceで隠蔽を定義しようとして悩む」のは、実はあなたが「外部からどう見えるか」を深く考えている証拠です。非常に良い思考の癖ですよ。

TypeScriptの型システムは、コードを「どう読み解くか」を定義し、JavaScriptの実行機能は「どう動くか」を定義します。この2つの層を分けて考えられるようになると、あなたのコードは一段上のレベルに到達します。

もし、クラスの設計で迷ったら、「これは外部との契約(Interface)に含まれるべきか?」と自問自答してみてください。それがクリアできれば、もうTypeScriptの設計で迷うことはありません。

それでは、良いコーディングライフを!

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