const assertionとは
Typescript 3.4 から導入された機能。
以下のように記述することで、オブジェクト・配列に対して再帰的に readonly を付与した型として扱ってくれる。
const val1 = {
value: 'hoge'
};
val1.value = 'fuga'; // これはエラーにならない
const val2 = {
value: 'hoge'
} as const;
val2.value = 'fuga'; // コンパイルエラー
// as constを使わずに同じことをすると以下になる。
const val3: { readonly value: 'hoge' } = {
value: 'hoge'
};
val3.value = 'fuga'; // こちらもコンパイルエラー
上記のようにas constを用いることで冗長な型指定をせずにreadonlyを付与、また型のwideningを抑制できる。
readonlyを付与していても書き換えができるパターン
readonlyが付与されていても同じ構造の型でreadonlyが指定されていない型に代入可能なため、
明示的に型のアサーションを行わずとも書き換え可能な型に代入ができる(配列が含まれない場合のみ、配列のときの挙動は後述)
たとえば以下のように。
const cval = { prop1: 'prop1!' } as const;
// cval.prop1 = ''; // これはコンパイルエラー
const val: { prop1: string } = cval; // コンパイルエラーにならない
val.prop1 = 'prop1?';
console.log(cval.prop1); // => prop1?
function f(val: { prop1: string }) {
val.prop1 = 'prop';
}
f(cval); // これもエラーにならない
console.log(cval.prop1); // => prop
配列が含まれている場合
readonlyがついた配列は動きが違い、readonlyではない配列に型アサーションなしで代入することはできない。
const cval2 = { arry: ['arry1'] } as const;
cval2.arry.push('arry2'); // これはコンパイルエラー
const val2: { arry: string[] } = cval2; // エラーになる
f2(cval2); // これもエラーになる
function f2(val2: { arry: string[] }) {
val2.arry.push('arry!');
}
const cval3 = ['str1', 'str2'] as const;
cval3.push('str'); // これはコンパイルエラー
const val3: string[] = cval3; // エラーになる
f3(cval3); // これもエラーになる
function f3(val3: string[]) {
val3.push('str');
}
Object.freezeの場合
Object.freezeの戻り値の型は Readonly<T>なので同様に代入が可能
ただし、実行時にエラーが発生する。
// Object.freezeを使った場合は実行時エラーになる。
const fval = Object.freeze({ prop: 'hoge' });
const val: { prop: string } = fval; // コンパイルエラーにならない
val.prop = 'xxx'; // 実行時にエラーが発生する
参考