TypeScriptのas constとは?具体的な使い方と2つの注意点
TypeScriptのコードを読んでいると、as const という見慣れない記述に出くわすことがあります。
as const って何をしているの? as はなんとなくわかるけど、const と合わせると意味がよくわからない。
as const を付けているコードを見たんだけど、付けない場合と何が違うんだろう?
as const を使う、って説明を読んだけど、なぜそっちのほうがいいの?
この記事では、as const(constアサーション)が何をしているのかを基礎から整理します。なぜ必要なのか、どんな効果があるのか、どんな場面で使うのかを順番に説明していきます。
この記事は次のような方におすすめです。
- TypeScriptのコードで
as constを見かけたが、何をしているかわからない人 - オブジェクトや配列にどんな効果があるか知りたい人
- enumの代わりに
as constを使う書き方を理解したい人 - 型推論がどう変わるのか、コード例でイメージしたい人
この記事を読むと、as const がなぜ必要か・どう動くかを理解でき、実際のコードに使えるようになります。
それでは、順を追って詳しく見ていきましょう!
- 未経験で後悔したくない
【実体験】未経験からITエンジニアに転職して後悔した話|4社経験してわかった「最初の選択ミス」 - 年収が低くて不安
4年間ずっと年収260万だったエンジニアが、転職で510万になるまでの全記録
as constとは何か(constアサーション)
as const は、TypeScriptに「この値はリテラルのまま固定してほしい」と伝える記法です。正式名称は「constアサーション(const assertion)」といいます。
なぜそういう記法が必要なのかを理解するには、まず「widening(型の拡大)」という現象を知っておく必要があります。
そもそもwideningとは?
TypeScriptはデフォルトで、値から型を推論するときに「少し広めの型」に推論することがあります。これをwidening(型の拡大)といいます。
変数への代入をコード例で見てみましょう。
// constで宣言した変数 → リテラル型のまま推論される
const direction = "left";
// type: "left" ← "left"というリテラル型
// letで宣言した変数 → string型に拡大される(widening)
let direction = "left";
// type: string ← "left"ではなくstringになる
const で宣言した変数は値が変わらないので、TypeScriptは "left" というリテラル型のまま推論してくれます。これは問題なさそうに見えます。
しかし、オブジェクトのプロパティは const で宣言しても widening が起きます。
// constで宣言していても、プロパティはwideningされる
const config = { direction: "left" };
// type: { direction: string } ← "left"ではなくstringになる
function move(dir: "left" | "right") {
console.log(dir);
}
move(config.direction);
// エラー: Argument of type 'string' is not assignable to parameter of type '"left" | "right"'
config.direction は明らかに "left" なのに、型は string として推論されるため、move 関数に渡せません。オブジェクトのプロパティは後から変更できてしまうので、TypeScriptは安全のために広い型で推論する、という動作になっています。
このwideningを防ぐために使うのが as const です。
as constを付けると型が固定される
as const を付けると、TypeScriptは「この値はリテラルのまま扱う」と認識します。
// as const を付けると型が固定される
const config = { direction: "left" } as const;
// type: { readonly direction: "left" } ← "left"のまま推論される
function move(dir: "left" | "right") {
console.log(dir);
}
move(config.direction); // OK! "left" 型として認識されるのでエラーなし
付ける場所はオブジェクトや配列の末尾です。
const obj = { ... } as const; // オブジェクトの末尾
const arr = [ ... ] as const; // 配列の末尾
const value = "left" as const; // リテラル値の末尾
as constの3つの効果
as const を付けると、主に3つの変化が起きます。順番に見ていきましょう。
①オブジェクトのプロパティをreadonlyにする
as const を付けると、オブジェクトのプロパティが readonly(読み取り専用) になり、かつプロパティの型が文字列・数値ではなくリテラル型に変わります。
// as const なし
const theme = {
color: "blue",
size: 16,
};
// type: { color: string; size: number }
theme.color = "red"; // エラーにならない(変更できてしまう)
// as const あり
const theme = {
color: "blue",
size: 16,
} as const;
// type: { readonly color: "blue"; readonly size: 16 }
theme.color = "red"; // エラー: Cannot assign to 'color' because it is a read-only property
string だった型が "blue" に、number だった型が 16 に変わっています。これにより、設定値やマスターデータのような「変えてはいけないオブジェクト」を型の力で守れるようになります。
ネストしたオブジェクトにも再帰的に適用されます。
const config = {
api: {
host: "example.com",
port: 8080,
},
} as const;
// type: { readonly api: { readonly host: "example.com"; readonly port: 8080 } }
深い階層まで自動的にreadonlyとリテラル型が適用されます。
②配列をreadonlyタプル型にする
配列に as const を付けると、string[] のような配列型ではなく、要素の型と順番が固定されたreadonlyタプル型になります。
// as const なし
const colors = ["red", "green", "blue"];
// type: string[] ← 要素はどれもstringとして扱われる
// as const あり
const colors = ["red", "green", "blue"] as const;
// type: readonly ["red", "green", "blue"] ← 各要素のリテラル型が保持される
string[] では個々の要素が "red" か "green" かを型として区別できませんが、タプル型になることで要素ごとのリテラル型が保持されます。後ほど紹介するUnion型の生成にも役立ちます。
③リテラル型のwideningを防ぐ
変数の宣言以外でも、wideningが起きる場面があります。たとえば 関数の戻り値では、明示的に型を書かないとwideningされることがあります。
// 戻り値の型にwideningが起きる
function getDirection() {
return "left";
// 戻り値の型: string ← wideningされる
}
// as const で固定する
function getDirection() {
return "left" as const;
// 戻り値の型: "left" ← リテラル型のまま
}
こうした場面では、as const を値の末尾に付けることでwidening を防げます。
as constの実践的な使い場面
効果がわかったところで、よく使われる実践的なパターンを2つ紹介します。
enumの代わりに使う
TypeScriptには enum という定数をまとめる構文がありますが、as const を使ってオブジェクトで代替する書き方も広く使われています。
// enumを使う書き方
enum Direction {
Left = "left",
Right = "right",
Up = "up",
Down = "down",
}
// as constを使った代替
const Direction = {
Left: "left",
Right: "right",
Up: "up",
Down: "down",
} as const;
// どちらも同じように使える
function move(dir: string) {}
move(Direction.Left); // "left"
as const を付けることで、プロパティの値がリテラル型として固定されるため、定数グループとして機能します。
enum との使い分けについては、こちらの記事で詳しく解説しています。
値からUnion型を作る
as const と typeof を組み合わせることで、オブジェクトや配列の値から自動的にUnion型を生成することができます。
const STATUS = ["active", "inactive", "pending"] as const;
// type: readonly ["active", "inactive", "pending"]
type Status = typeof STATUS[number];
// type: "active" | "inactive" | "pending"
typeof STATUS[number] は「STATUS の各インデックスにアクセスしたときの型」を意味します。as constがなければ STATUS の型は string[] になるため、このUnion型の生成はできません。
オブジェクトの値からUnion型を作ることもできます。
const Direction = {
Left: "left",
Right: "right",
Up: "up",
Down: "down",
} as const;
type DirectionType = typeof Direction[keyof typeof Direction];
// type: "left" | "right" | "up" | "down"
function move(dir: DirectionType) {
console.log(dir);
}
move(Direction.Left); // OK
move("left"); // OK
move("diagonal"); // エラー: 型が合わない
値の一覧とそこから派生するUnion型を1か所にまとめて管理できるので、値を追加・変更したときにUnion型も自動的に追従します。
as constを使うときの注意点
as const には便利な点がある一方、押さえておくべき制約が2つあります。
直接書いたリテラルにしか付けられない
as const が付けられるのは、その場に直接書いた文字列・数値・真偽値・配列・オブジェクトに限られます。変数に格納した後や、式の計算結果全体には付けられません。
// OK: その場に直接書いた値に付ける
const config = { host: "localhost" } as const;
const status = "active" as const;
// NG: 変数を経由する
const obj = { host: "localhost" };
const config = obj as const; // エラー
// NG: 式全体には付けられない
const x = (Math.random() < 0.5 ? 0 : 1) as const; // エラー
// OK: 式の中の各リテラルに個別に付ける
const x = Math.random() < 0.5 ? (0 as const) : (1 as const);
「直接書いたこの値をそのまま固定する」という記法なので、宣言と同時に付けるのが基本です。
外部変数を参照しているプロパティの中身は変更できる
as const はオブジェクトリテラルのプロパティをreadonlyにしますが、そのプロパティが外部変数を参照している場合、その変数の中身まで凍結されるわけではありません。
const arr = [1, 2, 3];
const obj = {
items: arr,
} as const;
// type: { readonly items: number[] }
obj.items = []; // エラー: readonly なので再代入は不可
obj.items.push(4); // OK: arr が number[] のままなので push は通る
obj.items の再代入はreadonlyで防げますが、items が指している配列自体は number[] の型を保ったままです。そのため push のような変更操作は型レベルで許容されてしまいます。
【付録】さらに学びを深めるためのリソース
さらにTypescriptの学習を進めたい方のために、いくつかのリソースを紹介します。
これらのリソースを活用することで、TypeScriptの型システムについてより深い知識を得ることができるでしょう。
おすすめの書籍
ゼロからわかる TypeScript入門
技術評論社から出版されている「ゼロからわかる TypeScript入門」は、プログラミング初心者や本職プログラマーではない方を主な対象にした入門書です。
変数・条件分岐・ループといった基本から、クラスやインターフェース、モジュールまで段階的に学べる構成になっています。最終章ではWeb APIとJSONを使った非同期Webアプリの作成も体験できるので、「実際に動くものを作る」ところまで到達できます。
プロを目指す人のためのTypeScript入門
技術評論社の「プロを目指す人のためのTypeScript入門 安全なコードの書き方から高度な型の使い方まで」、通称 ブルーベリー本 です。
JavaScriptの仕様とTypeScript独自の機能を両方押さえつつ、リテラル型・ユニオン型・keyof型・ジェネリクスなど、高度な型表現まで踏み込んで解説しています。TypeScriptの型システムの表現力を本格的に学べる一冊です。
オンラインで参照できる公式ドキュメント
TypeScript公式ハンドブック
https://www.typescriptlang.org/docs/
TypeScriptの公式ドキュメントです。
intersection型を含む、すべての型システムの機能について詳細な説明があります。
TypeScript Deep Dive
https://basarat.gitbook.io/typescript/
TypeScriptの深い部分まで掘り下げて解説しているオンラインブックです。
無料で読むことができ、intersection型についても詳しく説明されています。
TypeScriptの学習は終わりがありません。
新しい機能が常に追加され、より良い書き方が発見されています。
継続的に学習を続けることで、より良いTypeScriptプログラマーになれるはずです。
まとめ ― as constで型を固定する書き方
この記事の内容を整理します。
as const(constアサーション)は、値の型をリテラル型のまま固定する記法です- 普通にオブジェクトや配列を宣言すると widening(型の拡大)が起き、
stringやnumberに推論されてしまう場面があります as constを付けることで次の3つの効果が得られます- オブジェクトのプロパティが
readonlyになり、型がリテラル型になる - 配列が
readonlyタプル型になり、各要素の型が保持される - 関数の戻り値などでwideningを防げる
- オブジェクトのプロパティが
- よく使われる実践パターンとして、enumの代替とUnion型の自動生成があります
- ただし、直接書いたリテラルにしか付けられない点と、外部変数を参照しているプロパティの中身までは凍結されない点は覚えておきましょう
以上が as const の基本的な使い方と注意点です。「型が思ったより広く推論されてしまう」と感じた場面で、まず as const を試してみてください。
※本記事の本文案はAIを活用して作成していますが、記載している内容およびコードは筆者が実際に調査、検証・実行し、内容の正確性を確認した上で公開しています。






