結論からいえ
class Hoge {
constructor(
// どちらでも可能
private readonly mylist: readonly string[]
public readonly mylist2: readonly string[]
){
}
}
詳しく
readonly属性への理解
リストは基本的に「順番が保証されていて、操作可能な可変長の集合」を使いたいときに使うものです。
なので操作可能な事自体は必要なことなのですが…
まれに、このリストそのものを使わせてもよいが、変更は加えてほしくない、という場面に遭遇します。
そして思いつくのがこれです。
class Hoge {
constructor(
public readonly mylist: string[]
){
}
}
これでreadonlyなリストが宣言できました、というわけにはいきません。
class Hoge {
constructor(
public readonly mylist: string[]
){
}
}
const hoge = new Hoge(["0", "1", "2"])
hoge[0] = "3"
console.log(hoge)
// "3", "1", "2"
はい、mylistは普通に変更できてしまいます。
対して、これはエラーとなります。
class Hoge {
constructor(
public readonly mylist: string[]
){
}
}
const hoge = new Hoge(["0", "1", "2"])
hoge.mylist = ["3", "1", "2"] // error!
このことから、readonly属性はリスト内部の変更を制限しないことがわかります。
あくまで、その変数そのもの(今回はmylist)を変更することを制限するにとどまります。
readonly T[]
readonly T[]として宣言することで、typescriptコンパイラに、破壊的動作をするメソッドを
このlistは持たない、ということを宣言します。
class Hoge {
constructor(
public readonly mylist: readonly string[]
){
}
}
const hoge = new Hoge(["0", "1", "2"])
hoge.mylist[0] = ["3"] // error!
hoge.mylist.push("4") //error!
これにより、mylistが外部から汚染されることを免れます。
注意点
readonly T[]
はあくまでコンパイラに、破壊的メソッドが存在しないことを宣言する(隠す)だけであり、
強引に実行すると普通に実行される点に気をつけてください。
また、readonlyは再帰的ではありません。list内のオブジェクトがミュータブルな実装をしている場合、
値が変更されます。