TypeScriptのreadonlyを知ろう
概要
別記事を書くにあたりreadonlyを理解しようの会
readonlyとは
読み取り専用を示す型。
参考: https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes-func.html#readonly-and-const
constを用いた定数宣言で
const hoge = "hoge";
hoge = "fuga";
をすると 定数であるため、'hoge' に代入することはできません。
エラーが発生するが
const hoge = [1, 2, 3];
hoge = [1, 2]; // 🚫これはできない
hoge.push(4); // ✅プロパティの追加は可能
const fuga = { name: "ふが太郎", message: "こんにちは、ふが太郎です。" };
fuga = { name: "ふが次郎", message: "こんにちは、ふが次郎です。" }; // 🚫これはできない
fuga.name = "ふが次郎"; // ✅プロパティの変更は可能
などはできるようになっている。
これはJavaScriptのconstの仕様で
定数がオブジェクトであった場合、そのプロパティを追加したり、更新したり、削除したりすることができます。
となっているため。これをreadonlyを使ってできなくする。
type Hoge = readonly number[];
const hoge: Hoge = [1, 2, 3];
hoge.push(4);
type Fuga = { readonly name: string; readonly message: string };
const fuga: Fuga = { name: "ふが太郎", message: "こんにちは、ふが太郎です。" };
fuga.name = "ふが次郎";
とするとhoge.push()は プロパティ 'push' は型 'Hoge' に存在しません。
エラー、fuga.nameは 読み取り専用プロパティであるため、'name' に代入することはできません。
エラーになる。
したがって、readonlyを付けることで意図しない上書きを防ぐことができる。
readonlyをつけられるもの
readonlyは配列やタプル、オブジェクトのプロパティに使用できる。
type A = readonly string[]; // ✅配列型なのでOK
type B = readonly [string, number, boolean]; // ✅タプルの型なのでOK
type C = { readonly name: string }; // ✅オブジェクトのプロパティなのでOK
type D = readonly { name: string }; // 🚫オブジェクト自体にはNG
type E = readonly string; // 🚫配列やタプルやオブジェクトじゃないのでNG
type F = readonly 0 | 1 | boolean; // 🚫配列やタプルやオブジェクトじゃないのでNG
readonlyがあるものも代入できる
readonlyとして若干のつまづきポイントだったのでメモ。
以下のようにreadonly string[]
にstring[]
を使って定義することができる。
type Hoge = readonly string[];
const array: string[] = ["ほげ", "ふが"];
const hoge: Hoge = array;
readonlyは型の定義というよりかは定義した値の扱い方法を示すものなので、
readonlyの後ろの型に当てはまるのであればエラーにならない。
type Hoge = readonly string[];
const array: string[] = ["ほげ", "ふが"];
const hoge: Hoge = array;
あくまでもreadonlyを追記したら追加や変更ができないという理解をしておけばOK。
type Hoge = readonly string[];
const array: string[] = ["ほげ", "ふが"];
const hoge: Hoge = array;
hoge.push("ぴよ"); // 🚫readonlyなのでエラー
ちなみに
ネストがあるオブジェクトに対して
type Fuga = {
readonly name: string;
readonly message: string;
readonly birthday: { year: number; month: number; day: number };
};
const fuga: Fuga = {
name: "ふが太郎",
message: "こんにちは、ふが太郎です。",
birthday: { year: 2001, month: 1, day: 1 },
};
とした場合、fuga.birthdayの変更はできないがfuga.birthday.yearの変更はできてしまうため
type Fuga = {
readonly name: string;
readonly message: string;
readonly birthday: {
readonly year: number;
readonly month: number;
readonly day: number;
};
};
とする必要がある。
が、プロパティが増える度にreadonlyを付けて対応しなきゃいけなくて大変そうなので
const fuga = {
name: "ふが太郎",
message: "こんにちは、ふが太郎です。",
birthday: { year: 2001, month: 1, day: 1 },
} as const;
と as const を付けて手っ取り早く指定することが多いかも。
Discussion