Readonly<T>
最終更新:2026-10-22
Readonly<T> Readonly
Readonly<T> は T のすべてのプロパティを読み取り専用にします。オブジェクト生成後にプロパティへ代入しようとすると、コンパイラが即座にエラーを出します。設定オブジェクト、スナップショット、共有定数といった「作ったら変えられるべきでない」データに向いています。なお、これがロックするのはプロパティであり、const がロックする変数のバインディングとは別物です。
| グループ | ユーティリティ型 |
|---|---|
| 種類 | 型 |
| strict モード | 不要 |
ℹ️ 説明 エディタは TypeScript を JavaScript にトランスパイルして実行します(型チェックなし)。型チェック結果は各サンプルの出力を参照してください。
📝 構文
type Readonly<T> = {
readonly [K in keyof T]: T[K];
};
// 使い方
const config: Readonly<AppConfig> = { appName: "docs", port: 8080, debug: false };
// config.debug = true; // コンパイルエラー:Cannot assign to 'debug' because it is a read-only property
⚙️ パラメータ
| T | 変換されるオブジェクト型です。マップ型がそのすべてのプロパティをたどり、readonly 修飾子を付けます |
|---|
戻り値:新しい型です。プロパティの集合は T と同じですが、すべてに readonly の印が付きます。
▶ サンプル
下のコードを編集して「試す」を押すと、トランスパイル実行結果を確認できます:
出力:
docs-site:8080
(トランスパイル後の実行出力。読み取り専用の保護は tsc が静的に保証します。コメントを外した行は error TS2540 になり、素の JS そのものは代入を妨げません)
💡 ポイント
ℹ️ 説明 実装はマップ型で、[K in keyof T] に readonly を前置します。トランスパイル後は readonly が完全に消え、Object.freeze のような実行時の保護はありません。
⚠️ 注意 Readonly は浅いものです。ネストしたオブジェクトのプロパティは全体が読み取り専用になりますが、ネスト先の内部のフィールドは依然として変更可能です。配列のプロパティは ReadonlyArray になりますが、要素がオブジェクトならやはり変更できます。深い読み取り専用には DeepReadonly を自作しましょう。
💡 ヒント as const との違いはこうです。Readonly<T> は既存の型に作用します(リテラル推論は変わらず、mode は依然 string 型の値)。as const はリテラルそのものに作用します(mode は "dark" に絞り込まれます)。リテラルへのナローイングが必要なら as const、単に「変更を防ぎたい」だけなら Readonly を使いましょう。
🏷️ 関連ユーティリティ型
💡 関連ケース
ケースは今後追加予定です。
📚 関連チュートリアル
❓ よくある質問
Q Readonly と Object.freeze の違いは何ですか?
A Readonly は純粋にコンパイル時の保護で、トランスパイル後には消え、実行時の変更は成功します。Object.freeze は実行時の凍結で、静かに(厳格モードではエラーを出して)代入を阻止します。両方を併用できます。型には Readonly、実行時には freeze です。
Q readonly のプロパティは as アサーションで書き換えられますか?
A 迂回は可能です。(config as { debug: boolean }).debug = true はコンパイルが通ります。これはまさに readonly がコンパイル時の約束であって実行時の鎖ではないことを示しています。迂回は保護を自ら放棄する行為であり、チームのコードでは禁止すべきです。
Q ReadonlyArray と readonly T[] はどういう関係ですか?
A 完全に等価です。readonly T[] は ReadonlyArray<T> の略記形式です。どちらも push/pop/添字による代入を禁止します。Readonly<T> がオブジェクトのプロパティに効くのと同様、どちらも浅い読み取り専用の保護です。