TypeScriptの型はたくさんありますよね。
世界史みたいにカタカナがたくさん出てくる印象です。
そんでもって、こんな動きするのは覚えてるんだけど、これ名前なんだっけとなったりしています。(おそらく私だけ)
そんな中as const
についてちょこちょこ見かける機会がありました。
なんとなく読み取り専用になるイメージでしたが、改めて調べてみました。
今回はそこで学んだことを整理してみました。
as constとは
プロパティをすべてreadonlyにする型アサーションです。
もう少し詳しく見てみると、以下の特徴があります。
- 配列リテラルの型推論結果を配列型ではなくタプル型にする。
- オブジェクトリテラルから推論されるオブジェクト型はすべてのプロパティがreadonlyになる。配列リテラルから推論されるタプル型もreadonlyタプル型になる。
- 文字列・数値・BigInt・真偽値リテラルに対してつけられるリテラル型がwideningしないリテラル型になる。
- テンプレート文字列リテラルの型がstringではなくテンプレートリテラル型になる。
(鈴木 僚太. プロを目指す人のためのTypeScript入門 安全なコードの書き方から高度な型の使い方まで (Software Design plus) (Japanese Edition) (p.681)より引用)
実際にas const
をつけて型を作成してみました。
const setting = {
isDisabled: true,
size: 'L',
details: {
text: "詳細"
},
attrs: ["button", "input"]
} as const
type Setting = typeof setting
// Settingの中身
{
readonly isDisabled: true;
readonly size: "L";
readonly details: {
readonly text: "詳細";
};
readonly attrs: readonly ["button", "input"];
}
// as constを外した場合
{
isDisabled: boolean;
size: string;
details: {
text: string;
};
attrs: string[];
}
型の中身をas constの有無によって比較してみると、
as constがある方は全ての項目にreadonly
がついており、リテラル型が使用されていることがわかります。
配列にもタプル型が適用されており、厳格な型になっています。
readonlyとas constの違い
読み取り専用にする方法として、readonly
やReadonly<T>
を使用する方法があります。
type Setting = {
readonly isDisabled: Boolean
readonly size: 'S' | 'M' | 'L'
}
const setting: Setting = {
isDisabled: true,
size: 'L'
}
setting.isDisabled = false //readonlyのためエラーが発生する
readonly
とas const
ではどんな違いがあるのでしょうか。
その違いはreadonly
にする範囲にあります。
-
as const
はすべてのプロパティを読み取り専用にします -
readonly
はプロパティごとに読み取り専用できます
例えばプロパティの値がオブジェクトの場合、
オブジェクトのキーにreadonly
がついていなければ変更できてしまいます。
言葉だけだと難しいので、コードを書いてみました。
コードの中身はざっくり以下の通りです。
- Setting型があり、その中にはdetailsというプロパティがある。
- detailsはDetailsというオブジェクト型になっている。
type Details = {
text: string
}
type Setting = {
readonly isDisabled: Boolean
readonly size: 'S' | 'M' | 'L'
readonly details: Details
}
const setting: Setting = {
isDisabled: true,
size: 'L',
details: {
text: '詳細'
},
}
一見すべて読み取り専用に見えますが、
実は以下のような動作ができてしまいます。
//Details型のtextはreadonlyでないため、変更できてしまう。
setting.details.text = 'これは変えられる'
//detailsプロパティに対してreadonlyのため、detailsごと変更はできない。
setting.details = {
text: 'これはできないけど'
}
配列でも同様な動きとなります。
type Attrs = string[]
type Details = {
text: string
}
type Setting = {
readonly isDisabled: Boolean
readonly size: 'S' | 'M' | 'L'
readonly details: Details
readonly attrs: Attrs
}
const setting: Setting = {
isDisabled: true,
size: 'L',
details: {
text: '詳細'
},
attrs: ['button', 'input']
}
// Attrs型にreadonlyがないため、pushできてしまう
setting.attrs.push('hoge')
as const
は再帰的に見てくれるためプロパティがオブジェクトや配列の場合でも、すべての項目がreadonlyとなります。
const setting = {
isDisabled: true,
size: 'L',
details: {
text: '詳細'
},
} as const
// 以下のことはできなくなる
setting.details.text = 'これも変えられないし'
setting.details = {
text: 'こっちも変えられない'
}
setting.attrs.push('もちろんこれもできない')
参考
- プロを目指す人のためのTypeScript入門 安全なコードの書き方から高度な型の使い方まで (Software Design plus)
- サバイバルTypeScript constアサーション「as const」 (const assertion)