メインコンテンツまでスキップ

オブジェクトの型のreadonlyプロパティ (readonly property)

TypeScriptでは、オブジェクトのプロパティを読み取り専用にすることができます。読み取り専用にしたいプロパティにはreadonly修飾子をつけます。読み取り専用のプロパティに値を代入しようとすると、TypeScriptコンパイラーが代入不可の旨を警告するようになります。

ts
let obj: {
readonly foo: number;
};
obj = { foo: 1 };
obj.foo = 2;
Cannot assign to 'foo' because it is a read-only property.2540Cannot assign to 'foo' because it is a read-only property.
ts
let obj: {
readonly foo: number;
};
obj = { foo: 1 };
obj.foo = 2;
Cannot assign to 'foo' because it is a read-only property.2540Cannot assign to 'foo' because it is a read-only property.

readonlyは再帰的ではない

readonlyは指定したそのプロパティだけが読み取り専用になります。readonlyはそのオブジェクトが入れ子になっている場合、その中のオブジェクトのプロパティまでをreadonlyにはしません。つまり、再帰的なものではありません。

たとえば、fooプロパティがreadonlyで、foo.barプロパティがreadonlyでない場合、fooへの代入はコンパイルエラーになるものの、foo.barへ直接代入するのはコンパイルエラーになりません。

ts
let obj: {
readonly foo: {
bar: number;
};
};
obj = {
foo: {
bar: 1,
},
};
obj.foo = { bar: 2 };
Cannot assign to 'foo' because it is a read-only property.2540Cannot assign to 'foo' because it is a read-only property.
obj.foo.bar = 2; // コンパイルエラーにはならない
ts
let obj: {
readonly foo: {
bar: number;
};
};
obj = {
foo: {
bar: 1,
},
};
obj.foo = { bar: 2 };
Cannot assign to 'foo' because it is a read-only property.2540Cannot assign to 'foo' because it is a read-only property.
obj.foo.bar = 2; // コンパイルエラーにはならない

再帰的にプロパティを読み取り専用にしたい場合は、子や孫の各プロパティにreadonlyをつけていく必要があります。

ts
let obj: {
readonly foo: {
readonly bar: number;
};
};
ts
let obj: {
readonly foo: {
readonly bar: number;
};
};

readonlyはコンパイル時のみ

readonlyはTypeScriptの型の世界だけの概念です。つまり、読み取り専用指定を受けたプロパティがチェックを受けるのはコンパイル時だけです。コンパイルされた後のJavaScriptとしては、readonlyがついていたプロパティも代入可能になります。

たとえば、fooプロパティをreadonly指定したコードで、fooに代入するコードはコンパイル時にはエラーとして検出されます。

ts
const obj: { readonly foo: number } = { foo: 1 };
obj.foo = 2; // コンパイルエラーになる
Cannot assign to 'foo' because it is a read-only property.2540Cannot assign to 'foo' because it is a read-only property.
ts
const obj: { readonly foo: number } = { foo: 1 };
obj.foo = 2; // コンパイルエラーになる
Cannot assign to 'foo' because it is a read-only property.2540Cannot assign to 'foo' because it is a read-only property.

しかし、コンパイル後のJavaScriptコードでは、readonlyの記述がなくなるので、実行時にエラーとして検出されることはありません。

コンパイル後のJavaScriptコード
ts
const obj = { foo: 1 };
obj.foo = 2; // 実行時エラーにはならない
 
コンパイル後のJavaScriptコード
ts
const obj = { foo: 1 };
obj.foo = 2; // 実行時エラーにはならない
 

実行時にチェックが無いことは一見すると危険そうですが、コンパイルエラーを無視せず、ちゃんと修正しておけば大きな問題になることはありません。

すべてのプロパティを一括して読み取り専用にする方法

TypeScriptではプロパティを読み取り専用にするには、読み取り専用にしたい各プロパティにひとつひとつreadonly修飾子をつける必要があります。プロパティ数が多くなるとreadonlyをつけていくのは記述量が多くなり手間です。

そういったケースではユーティリティ型のReadonlyを使うのも手です。Readonlyはプロパティをすべて読み取り専用にしてくれる型です。

ts
let obj: Readonly<{
a: number;
b: number;
c: number;
d: number;
e: number;
f: number;
}>;
ts
let obj: Readonly<{
a: number;
b: number;
c: number;
d: number;
e: number;
f: number;
}>;

📄️ Readonly<T>

全プロパティを読み取り専用にする

関連情報

📄️ クラスのreadonly修飾子

TypeScriptでは、フィールドにreadonly修飾子をつけると、そのフィールドを読み取り専用にできます。

📄️ インターフェースのreadonly修飾子

TypeScriptのインターフェースでは、フィールドにreadonly修飾子をつけることで読み取り専用のフィールドが定義できます。

📄️ 読み取り専用の配列

TypeScriptでは配列を読み取り専用(readonly)として型注釈できます。型注釈の方法は2通りあります。1つ目はreadonlyキーワードを使う方法です。2つ目はReadonlyArrayを使う方法です。

📄️ constアサーション「as const」

オブジェクトリテラルの末尾にas constを記述すればプロパティがreadonlyでリテラルタイプで指定した物と同等の扱いになります。