項目14 readonlyを使用して変更にまつわるエラーを避ける
readonly修飾子とReadonly<T>
オブジェクトのプロパティにreadonlyをつけることで、そのプロパティに代入できなくなる
code:ts
interface PartlyMutableName {readonly first: string; last: string;}
const jackie: PartlyMutableName = { first: 'Jacqueline', last: 'Kennedy' };
jackie.last = 'Onassis'; // OK
jackie.first = 'Jacky'; // エラー
code:ts
interface FullyMutableName {first: string;last: string;}
type FullyImmutableName = Readonly<FullyMutableName>;
注意点
効果が浅いこと
code:ts
interface Outer {inner: {x: number;}}
const obj: Readonly<Outer> = { inner: { x: 0 }};
obj.inner = { x: 1 }; //
obj.inner.x = 1; // OK
readonlyは付与されているプロパティのみにしか適応されない
ネストしたオブジェクトのプロパティすべてを普遍にしたければ、すべてのプロパティに付与する必要がある
プロパティにしか影響しないこと
オブジェクトを変更するメソッドを持つ型に適応しても、効果はない
code:ts
const date: Readonly<Date> = new Date();
date.setFullYear(2037); // dateの値が変更される
イミュータブルなインターフェース
一般的に、あるクラスのミュータブルバージョンとイミュータブルバージョンのインターフェースは別々に定義する必要がある
Array<T> :T[]
ReadonlyArray<T> : readonly T[]
T[]はreadonly T[]のサブタイプ
T[]の方ができることが多いため、readonly T[]の拡張としてみなされる
関数パラメータをreadonlyにする
関数がパラメータを変更しないなら、そのようなパラメータはreadpnlyで宣言すべき
code:ts
function arraySum(arr: readonly number[]) {
let sum = 0, num;
while ((num = arr.pop()) !== undefined) { // arrはreadonlyのためpopを使って変更できない
sum += num;
}
return sum;
}