Menu

TypeScriptのリテラル型と as const をサンプルで解説

リテラル型とは、"GET" や 404 のように値がちょうど1つしかない型です。文字列・数値・boolean のリテラル型、リテラル型のユニオン、let は型が広がり const は広がらない理由、as const の働き、const 型パラメーターを解説します。

このページのコードはエディタで実行できます - 編集してすぐに結果を確認できます。

リテラル型とは、値がちょうど1つしかない型です。"up" は文字列 "up" だけをメンバーに持つ型で、404 は数値 404 だけをメンバーに持つ型です。単独ではあまり役に立ちませんが、ユニオン型にまとめると、決まった値の集合だけを受け付ける変数が作れます。

@ts-expect-error コメントがなければ、最後の呼び出しはコンパイルエラー(TS2345)です。コメントがあればプログラムはコンパイルでき、呼び出しも実行されて moving north by 1 が出力されます。リテラル型はコンパイラーのためだけに存在し、実行時の値は普通の文字列だからです。

文字列・数値・boolean のリテラル型

どんな文字列、数値、bigint、boolean の値も型として書けます。コンパイラーはその値ちょうどだけを受け付けます。

let method: "GET" = "GET";
let code: 404 = 404;
let ok: true = true;
let big: 10n = 10n;

type Port = 80 | 443 | 8080;
const port: Port = 443;

boolean 自体もただのユニオン型 true | false です。そのため、boolean を if (flag) で絞り込むと else のブランチには false が残ります。

リテラル型許す値広い型
"GET"文字列 "GET" だけstring
404数値 404 だけnumber
10nbigint の 10 だけbigint
truetrue だけboolean

リテラル型のユニオン

よくある使い方は、許可するすべての値を並べたユニオン型です。関数の中でチェックするたびにコンパイラーがユニオン型を絞り込むので、各ブランチは自分がどの値を持っているかを正確に知っています。

文字列リテラルのユニオン型は、TypeScriptでの enum の代わりとしてよく使われます。実行時のコストはなく、値はログに出したりJSONで送ったりできる普通の文字列で、打ち間違いはコンパイルエラーになります。長所と短所について詳しくは enum を参照してください。

型の拡大: let と const

TypeScriptはリテラルから型を推論するとき、値が変わりうるかどうかを見ます。const の変数は再代入できないので、リテラル型を保ちます。let の変数は再代入できるので、型は一般的な型に広げられます。

エディターで各名前にカーソルを合わせると、推論された型が確認できます。特定の値だけを持つ let にしたいなら、型注釈を付けます: let mode: "light" | "dark" = "light"。

オブジェクトのプロパティの型が広がる理由

オブジェクトリテラルのプロパティは変更可能なので、オブジェクトを const に入れても型は広がります。リテラル型に思いがけず出くわす、いちばんよくある場面です:

コンパイラーは次のエラーを報告します:

index.ts(7,15): error TS2345: Argument of type 'string' is not assignable to parameter of type '"GET" | "POST"'.

あとのコードが req.method = "DELETE" を実行するかもしれないので、req は { url: string; method: string } と推論されます。直し方は3つあります:

4つ目の方法は satisfies で、リテラルのプロパティの型を保ったまま、オブジェクトを型と照らし合わせてチェックします。

as const

as const は const アサーションです。式のあとに付けると、コンパイラーはできるだけ狭い型を推論します:

  • 文字列、数値、boolean の値はリテラル型のまま
  • オブジェクトのプロパティは readonly になる
  • 配列リテラルは長さが固定された readonly のタプルになる

アサーションはコンパイル時だけのものです。出力されるJavaScriptは as const を取り除いた同じオブジェクトリテラルなので、実行時にほかのコードが変更するのを止めるものはありません。実行時の保証が必要なら、Object.freeze も呼びましょう。

as const の配列からユニオン型を作る

よくあるパターンは、許可する値を実行時にループできる1つの配列に置き、そこからユニオン型を作ることです。(typeof arr)[number] は「arr の任意の要素の型」を意味します。

as const がなければ、ROLES は string[] で、Role はただの string になります。isRole の中で readonly string[] にキャストしているのは、リテラルのタプルに対する includes はそのリテラルしか受け付けず、この関数の目的はそれに当てはまらないかもしれない文字列をテストすることだからです。同じパターンはキーと値の対応にも使えます: const Status = { Active: "active", Banned: "banned" } as const として、type Status = (typeof Status)[keyof typeof Status] と書きます。

const 型パラメーター

ジェネリック関数は、ふつう渡されたリテラルの型を広げます。TypeScript 5.0 以降は型パラメーターに const を付けられ、呼び出し側に as const を書かせることなく、引数に as const が付いているかのように推論させられます。

これは主にライブラリの作者のための道具です。ルート定義、ビルダー、スキーマのヘルパーがこれを使い、呼び出し側は普通のリテラルから正確な型を得られます。

const のさまざまな意味

TypeScriptのコードでは、const というキーワードが4つの異なる場所に出てきます:

構文種類働き
const x = 1JavaScriptの宣言変数を再代入できない。リテラルの値はリテラル型を保つ
expr as constTypeScriptのアサーションいちばん狭い型: リテラル、readonly プロパティ、readonly タプル
function f<const T>()TypeScriptの型パラメーター引数を as const が付いているかのように推論する
const enum E {}TypeScriptの enumメンバーがコンパイル時にインライン展開される enum

どれも実行時にオブジェクトを凍結はしません。const obj = { a: 1 } でも obj.a = 2 はでき、エラーになるのは obj 自体の再代入だけです。

よくある質問

TypeScriptのリテラル型とは何ですか?

ちょうど1つの値だけを許す型です。"GET" は文字列 "GET" だけを値に持つ型、404 は数値 404 だけを値に持つ型、true は true だけを値に持つ型です。type Method = "GET" | "POST" のようにユニオン型にまとめると最も役に立ちます。

TypeScriptの as const は何をしますか?

as const は const アサーションです。式に対していちばん狭い型を推論するようコンパイラーに伝えます。文字列や数値の値はリテラル型のまま、オブジェクトのプロパティは readonly に、配列リテラルは readonly のタプルになります。変わるのは型だけで、実行時の値は普通のオブジェクトや配列のままで、凍結もされません。

TypeScriptがリテラルではなく string と推論するのはなぜですか?

値が変更可能だからです。let x = "a" や { method: "GET" } の中のプロパティはあとで再代入できるので、TypeScriptは string に広げます。const の変数はリテラル型 "a" を保ちます。オブジェクトの中でリテラルを保つには、リテラル型で型注釈を付けるか、as const か satisfies を使います。

const と as const の違いは何ですか?

const はJavaScriptの宣言で、変数は再代入できませんが、それが指すオブジェクトは変更できます。as const はTypeScriptの型アサーションで、型システムの中で値のすべてのプロパティを readonly かつリテラルにします。どちらも実行時にオブジェクトを凍結はしません。それには Object.freeze を使います。

文字列の配列からユニオン型を作るには?

配列を as const で宣言し、その型に number でインデックスアクセスします: const roles = ["admin", "user"] as const; type Role = (typeof roles)[number]; で "admin" | "user" が得られます。as const がなければ配列は string[] で、結果はただの string になります。

Coddy programming languages illustration

Coddyでコードを学ぼう

始める