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

オブジェクト型の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をつけていく必要があります。

typescript
let obj: {
readonly foo: {
readonly bar: number;
};
};
typescript
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はプロパティをすべて読み取り専用にしてくれる型です。

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

関連情報#