🎱

【Typescript】as const って結局何?調べたことをまとめてみた

に公開

✍️ はじめに

実務で 型アサーション= as const を使うことはよくあります。実務でも as const を使用しているコードをよく見たり、自分も使っていましたが今まで深く考えずに使っていました。
最近、『【初学者向け】具体例で学ぶTypeScript練習問題集』の問題を解いているときに、以下の問題に出会いました:
【Lv.3】オブジェクトのインデックス
そのときふと、「そもそも as const って何をしてるんだっけ?」「なぜ付けたほうがいいの?」と疑問に思いました。
本記事では as const の役割と、通常の型推論との違いを整理していきます。

🔍 as const の効果とは?

const statusColors = {
  success: "green",
  error: "red",
  warning: "yellow"
}; // 通常の型推論(string型)

この場合、TypeScript は以下のように推論します:

const statusColors: {
  success: string;
  error: string;
  warning: string;
}

一見問題なさそうですが、statusColors.success は "green" であることが明らかなのに、型としては string になってしまっています。
ここで as const を付けると、型は次のように変わります:

const statusColors = {
  success: "green",
  error: "red",
  warning: "yellow"
} as const; // リテラル型 + readonly になる
const statusColors: {
  readonly success: "green";
  readonly error: "red";
  readonly warning: "yellow";
}

💡 何が変わったのか?

  • 値の型が string ではなく、"green" や "red" といったリテラル型に変わった
  • 各プロパティに readonly が付き、変更不可(イミュータブル)になる

この違いにより、TypeScript がより厳密に型を扱えるようになります。
特に注目したいのは、値がリテラル型になるという点です。
今までは readonly によって変更を防げるというメリットばかり意識しがちでしたが、
リテラル型になることで Union 型として抽出できるようになり、型安全な処理がしやすくなります。
これは実務でのバリデーションやキーの制約、表示の切り替えなどにも活用できる大きな利点です。

🎨 具体例:ステータスごとの色マッピング

ここでは、ステータスに対応する色を定義したオブジェクトを例にとります。

const statusColors = {
  success: "green",
  error: "red",
  warning: "yellow",
} as const;
// キーの型を抽出
type Status = keyof typeof statusColors; // "success" | "error" | "warning"
// 値の型を抽出
type Color = typeof statusColors[Status]; // "green" | "red" | "yellow"

このように as const を使うことで、オブジェクトの値を具体的なリテラル型として扱えるため、Union 型として抽出することが可能になります。

⚠️ as const を付ければ何でも安全なのか?注意すべきケースを考える

ここまで as const のメリットについて考えてきました。
ただし、注意すべき点がないのかも検討する必要があります。
as const を付けたからといって、すべてのバグを防げるわけではありません。
型が厳密になりすぎて逆に扱いにくくなることもあるので、状況に応じて使い分ける必要があります。
例えば次のようなケースを考えてみましょう:

const directions = ["up", "down"] as const;

function move(dir: string) {
  console.log(dir);
}

move(directions[0]); // 問題なく動作します

この例では、directions[0] の型は "up" というリテラル型になりますが、リテラル型は対応する一般的な型(この場合は string)に自動的に割り当て可能です。つまり、リテラル型 "up"string 型に問題なく割り当てられます。

一方、逆のケースでは問題が発生します:

const directions = ["up", "down"] as const;
type Direction = typeof directions[number]; // "up" | "down"

function moveStrict(dir: Direction) {
  console.log(dir);
}

const someString: string = "up";
moveStrict(someString); // エラー: 型 'string' を型 '"up" | "down"' に割り当てることはできません

この場合、someString は一般的な string 型であり、実行時に "up""down" 以外の値を持つ可能性があるため、より具体的な Direction 型に割り当てることはできません。

📝 まとめ

  • as const は値をリテラル型として固定し、かつ readonly を付けてくれる
  • 型推論が「string」や「number」などの柔らかい型になるのを防ぎたいときに使う
  • Union 型の抽出や、型安全なマッピングにとても便利
    何気なく使っていた as const ですが、その意図を知ることで TypeScript をより強力に、安全に活用できるようになります。

🙇‍♂️ 参考記事

本記事は以下のZenn記事から多くの気づきを得て執筆しました。とても勉強になりました。ありがとうございます!
https://zenn.dev/kagan/articles/typescript-practice

Discussion