8
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

as constについて整理してみる

Last updated at Posted at 2022-08-27

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の違い

読み取り専用にする方法として、readonlyReadonly<T>を使用する方法があります。

type Setting = {
	readonly isDisabled: Boolean
	readonly size: 'S' | 'M' | 'L'
}

const setting: Setting = {
	isDisabled: true,
	size: 'L'
}

setting.isDisabled = false //readonlyのためエラーが発生する

readonlyas 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('もちろんこれもできない')

参考

8
3
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?