TypeScript の readonly な型と仕様
TypeScript で、readonly な仕組みとしてどのような型や仕様があるのか気になり調べました。見つけた範囲で書きます。
試したコードは gist にあります: TypeScript readonly
目次:
※以降のコードには、readonly なのでこのコードは書けない、ということを示すため、コンパイルエラーとなる記述が含まれます。同じ行のコメントに「// Error:」とかいてあるものがコンパイルエラーとなる行です。
Read-only properties
readonly 修飾子は TypeScript 2.0 でリリースされたようです(参照)
interface Foo { readonly bar: number; readonly bas: number; } const foo: Foo = { bar: 1, bas: 2 }; foo.bar = 11; // Error: Cannot assign to 'bar' because it is a read-only property
class Person { constructor(readonly name: string) { } write() { this.name = 'new name'; // Error: [ts] Cannot assign to 'name' because it is a read-only property. [2540] } }
const value: { readonly name: string } = { name: 'name' }; value.name = 'new name'; // Error: [ts] Cannot assign to 'name' because it is a read-only property. [2540]
readonly に限った話ではないですが、型を崩せば値を変更できてしまいます。
(foo as { bar: number }).bar = 999; (foo as any).bar = 999;
- Read-only properties and index signatures - What's new in TypeScript · Microsoft/TypeScript Wiki
- Classes · TypeScript
- Readonly - TypeScript Deep Dive 日本語版
Read-only index signatures
const dic: { readonly [key: number]: number } = { 0: 100, 1: 101, 2: 102, }; dic[103] = 103;// Error Index signature in type '{ readonly [key: number]: number; }' only permits reading.
Readonly<T>
Readonly<T> は、2.1 から使えます。参考: TypeScript 2.1 · TypeScript
interface Category { name: string; [key: number]: number; } let category1: Readonly<Category> = { name: 'category1' }; category1 = { name: 'new cateogry name' }; // ok category1.name = 'new name'; // Error: [ts] Cannot assign to 'name' because it is a read-only property. [2540] category1[100] = 100; // Error: [ts] Index signature in type 'Readonly<Category>' only permits reading. [2542]
const state1: Readonly<{ name: string }> = { name: 'name' }; state1.name = 'new name';// Error: Cannot assign to 'name' because it is a read-only property.
sample:
function read<T extends Category>(value: Readonly<T>) { value.name = 'new name';// Error: Cannot assign to 'name' because it is a read-only property. }
以下の記述は 2.8 からです。参考: Improved control over mapped type modifiers
type ReadonlyCategory = { +readonly [P in keyof Category]: Category[P] }; const category2: ReadonlyCategory = category1; category2.name = 'new name'; // Error: [ts] Cannot assign to 'name' because it is a read-only property. [2540] category2[100] = 100; // Error: [ts] Index signature in type 'Readonly<Category>' only permits reading. [2542] type MutableCategory = { -readonly [P in keyof ReadonlyCategory]: ReadonlyCategory[P] }; const category3: MutableCategory = category2; category3.name = 'new name';// ok category3[100] = 100;// ok
ReadonlyArray<T>
ReadonlyArray<T>、ReadonlyMap<T>、ReadonlySet<T> は、readonly 修飾子と同じタイミングの 2.0 から使えます。
const numbers = [1, 2, 3]; numbers.push(4); const numbers2: ReadonlyArray<number> = numbers; numbers2.push(5);// Error: [ts] Property 'push' does not exist on type 'ReadonlyArray<number>'. [2339] numbers2[0] = 4;// Error: [ts] Index signature in type 'ReadonlyArray<number>' only permits reading. [2542]
ReadonlyMap<T>
const map = new Map<string, string>(); map.set("key1", "value1"); const readonlyMap: ReadonlyMap<string, string> = map; readonlyMap.set("key1", "value1");// Error: Property 'set' does not exist on type 'ReadonlyMap<string, string>'.
ReadonlySet<T>
const set = new Set<string>(); set.add('value'); const readonlySet: ReadonlySet<string> = set; readonlySet.add('value;');// Error: [ts] Property 'add' does not exist on type 'ReadonlySet<string>'.
追記 2019/3/18
- TypeScript | Announcing TypeScript 3.4 RC
- 3.4 RC で、readonly 周りの仕様の追加が予定されています。