読み取り専用の配列 (readonly array)
TypeScriptでは配列を読み取り専用(readonly)として型注釈できます。型注釈の方法は2通りあります。1つ目はreadonly
キーワードを使う方法です。2つ目はReadonlyArray<T>
を使う方法です。
readonly T[]
配列の型注釈T[]
の前にreadonly
キーワードを添えると、読み取り専用の配列型にできます。たとえば、readonly number[]
と書くと、その変数の型はnumberの読み取り専用配列型になります。
ts
constnums : readonly number[] = [1, 2, 3];
ts
constnums : readonly number[] = [1, 2, 3];
ReadonlyArray<T>
ReadonlyArray<T>
のような書き方でも読み取り専用の配列型になります。たとえば、要素がnumber型の配列を読み取り専用にしたい場合、ReadonlyArray<number>
と書きます。
ts
constnums :ReadonlyArray <number> = [1, 2, 3];
ts
constnums :ReadonlyArray <number> = [1, 2, 3];
readonly T[]とReadonlyArray<T>の違い
readonly T[]
とReadonlyArray<T>
の違いは書き方以外にありません。どちらを使うかは書き手の好みです。開発チームとしてはどちらの書き方にするかは統一しておいたほうがよいでしょう。
読み取り専用配列の特徴
読み取り専用の配列には、配列に対して破壊的操作をするpush
メソッドやpop
メソッドが、コンパイル時には無いことになります。したがって、readonly number[]
型の変数nums
に対して、nums.push(4)
をするコードはコンパイルエラーになります。
ts
constnums : readonly number[] = [1, 2, 3];Property 'push' does not exist on type 'readonly number[]'.2339Property 'push' does not exist on type 'readonly number[]'.nums .(4); push
ts
constnums : readonly number[] = [1, 2, 3];Property 'push' does not exist on type 'readonly number[]'.2339Property 'push' does not exist on type 'readonly number[]'.nums .(4); push
これは、破壊的操作系のメソッドを呼び出そうとするコードがTypeScriptコンパイラーに警告されるだけです。配列オブジェクトからpush
メソッドを削除しているわけではありません。なので、JavaScript実行時にはpush
メソッドが残っている状態になります。
ts
constnums : readonly number[] = [1, 2, 3];console .log ("push" innums );
ts
constnums : readonly number[] = [1, 2, 3];console .log ("push" innums );
メソッドは削除されるわけではないので、コンパイルエラーを無視して実行してみると、読み取り専用型でも配列を書き換えることはできます。
ts
constnums : readonly number[] = [1, 2, 3];// @ts-ignorenums .push (4); // 本来コンパイルエラーになるが無視するconsole .log (nums );
ts
constnums : readonly number[] = [1, 2, 3];// @ts-ignorenums .push (4); // 本来コンパイルエラーになるが無視するconsole .log (nums );
読み取り専用配列を配列に代入する
TypeScriptの読み取り専用配列を普通の配列に代入することはできません。代入しようとするとコンパイルエラーになります。
ts
constreadonlyNumbers : readonly number[] = [1, 2, 3];constThe type 'readonly number[]' is 'readonly' and cannot be assigned to the mutable type 'number[]'.4104The type 'readonly number[]' is 'readonly' and cannot be assigned to the mutable type 'number[]'.: number[] = writableNumbers readonlyNumbers ;
ts
constreadonlyNumbers : readonly number[] = [1, 2, 3];constThe type 'readonly number[]' is 'readonly' and cannot be assigned to the mutable type 'number[]'.4104The type 'readonly number[]' is 'readonly' and cannot be assigned to the mutable type 'number[]'.: number[] = writableNumbers readonlyNumbers ;
これは、普通の配列はpush
やpop
などのメソッドが必要なのに、読み取り専用配列にはそれが無いことになっているためです。どうしても読み取り専用配列を普通の配列に代入したいときは、型アサーション(type assertion)を使う方法があります。
ts
constreadonlyNumbers : readonly number[] = [1, 2, 3];constwritableNumbers : number[] =readonlyNumbers as number[];
ts
constreadonlyNumbers : readonly number[] = [1, 2, 3];constwritableNumbers : number[] =readonlyNumbers as number[];
📄️ 型アサーション「as」
TypeScriptには、型推論を上書きする機能があります。その機能を型アサーション(type assertion)と言います。
逆のパターンとして、普通の配列を読み取り専用配列に代入することは可能です。
関連情報
📄️ 配列の破壊的操作
JavaScriptの配列メソッドには、破壊的なメソッドと非破壊的なメソッドの2種類があります。特に、破壊的なメソッドは注意深く使う必要があります。
📄️ オブジェクト型のreadonlyプロパティ
TypeScriptでは、オブジェクトのプロパティを読み取り専用にすることができます。読み取り専用にしたいプロパティにはreadonly修飾子をつけます。読み取り専用のプロパティに値を代入しようとすると、TypeScriptコンパイラーが代入不可の旨を警告するようになります。
📄️ Readonly<T>
全プロパティを読み取り専用にする
📄️ constアサーション「as const」
オブジェクトリテラルの末尾にas constを記述すればプロパティがreadonlyでリテラルタイプで指定した物と同等の扱いになります。