【TypeScript】as const の使いどころ・注意点まとめ

# はじめに

この記事では、TypeScriptにおける **`as const`** の概要や使いどころ、注意点などについてサンプルコードを交えながらまとめます。

https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#literal-types

# `as const` とは？

TypeScriptの `as const` は、リテラル型をそのまま保持するための型アサーション（型の断定）です。

通常、配列やオブジェクトを定義すると、その中身はより広い型に推論されますが、`as const` を使うことで 中身の型をリテラルに固定し、さらに readonly にする ことができます。

▼ 例：配列の場合

```.ts
const arr = [1, 2, 3];
// 推論: number[]

const fixedArr = [1, 2, 3] as const;
// 推論: readonly [1, 2, 3]
```

▼ 例：オブジェクトの場合

```.ts
const obj = { role: "admin" };
// 推論: { role: string }

const fixedObj = { role: "admin" } as const;
// 推論: { readonly role: "admin" }
```


# 主な用途
固定された選択肢を型として定義したいときに非常に便利です。

例えば、色を選ぶフォームの選択肢や、APIが  `"red" | "green" | "blue"` のような固定値（enum的な値）を返す場合に便利です。

```.ts
const colors = ["red", "green", "blue"] as const;
// 推論: readonly ["red", "green", "blue"]

type Color = typeof colors[number];
// Color型: "red" | "green" | "blue"
```

この `Color` 型を使えば、UIでの選択やバリデーション、APIとの整合性を型で保証できます。

▼ 使用例1: セレクトボックスの選択値に使う
```.ts
function onSelectColor(color: Color) {
  console.log(`選ばれた色は ${color} です`);
}

onSelectColor("red");   // ✅ OK
onSelectColor("yellow"); // ❌ エラー: 型 '"yellow"' を型 'Color' に割り当てることはできません。

```

▼ 使用例2: APIから受け取った色が有効か判定する
```.ts
function isValidColor(value: string): value is Color {
  return colors.includes(value as Color);
}

const fromApi = "green";

if (isValidColor(fromApi)) {
  // fromApi の型はここで Color に絞られる
  console.log(`APIから受け取った色: ${fromApi}`);
} else {
  console.error("不正な色の値を受信しました");
}

```

:::note
`typeof colors[number]` の `[number]` について

- `typeof colors`
  - `colors` 配列の型そのものを取得
  - `as const` を使用しているため、TypeScript は `colors` を `readonly ["red", "green", "blue"]` という固定されたタプルとして推論
- `[number]`
    - 配列やタプルの型に対して `[number]` をつける
    - 配列やタプルを数値のインデックス（例: `colors[0]`、`colors[1]`）でアクセスした場合に返されるすべての要素の型を結合したユニオンを作成

`[number]` がないと、`typeof colors` は `readonly ["red", "green", "blue"]` となり、これは配列そのものの型です。

`[number]` を加えることで、TypeScript は「`colors` 配列に含まれる可能性のあるすべての要素の型」を抽出し、結果として `"red" | "green" | "blue"` というユニオン型を生成します。これにより、`Color` 型は「`"red"、"green"、"blue"` のいずれかの文字列」を表現できるようになります。

:::

# 注意点
`as const` を使うと、配列やオブジェクトのプロパティは自動的に `readonly` になります。

```.ts
const list = [1, 2, 3] as const;
list.push(4); // ❌ エラー: Property 'push' does not exist on type 'readonly [1, 2, 3]'
```

そのため、ミューテート（変更）する前提のデータには使えません。


# Tips
`as const` に関する Tips を紹介します。

## 1. 毎回 `as const` を書くのが面倒？ → const アサーションヘルパーの活用
`as const` は便利ですが、使うたびに毎回手で書くのは少し煩わしいと感じるかもしれません。
特にユニオン型を作るための配列を何度も定義するようなケースでは、次のように繰り返し `as const` を書くことになります。

```.ts
const colors = ["red", "green", "blue"] as const;
const sizes = ["small", "medium", "large"] as const;
const roles = ["admin", "user", "guest"] as const;
```

このようなパターンが増えてくると、毎回 `as const` を書く手間や統一されていない記述が気になってきます。
そんなときは以下のようなユーティリティ関数を 1 つ定義しておくと、定数配列を簡潔かつ一貫性のある方法で定義できます。

```.ts
// ユニオン型を得るための定数配列を定義するヘルパー関数
const defineConstArray = <T extends readonly string[]>(arr: T) => arr;
```

これを使えば、`as const` を意識せずに済みます。

```.ts
const categories = defineConstArray(["news", "event", "release"]);
// 推論: readonly ["news", "event", "release"]

type Category = typeof categories[number];
// 型: "news" | "event" | "release"

```


## 2. バリデーションライブラリとの連携
`as const` を使って定義したユニオン型を、Zodなどのバリデーションライブラリでも活用できます。

https://zod.dev/

`as const` を使えば、Zodのスキーマと型を一箇所で定義できます。

```.ts
import { z } from "zod";

// まず定数配列を定義
const colors = ["red", "green", "blue"] as const;

// スキーマは配列から直接生成できる
const ColorSchema = z.enum(colors);

// 型もスキーマから取得可能（または typeof colors[number] でもOK）
type Color = z.infer<typeof ColorSchema>;

```

`z.enum(colors)` は、`readonly ["red", "green", "blue"]` の配列を受け取って Zod の enum スキーマを作ります。型 `Color` は `"red" | "green" | "blue"` として推論され、フォーム入力や API レスポンスの型としても活用できます。
こうすることで、定義の重複を避けつつ、型とバリデーションの整合性が取れるようになります。

# まとめ
`as const` は、TypeScriptでリテラル型を固定し、`readonly` な定数として扱うための便利な機能です。配列やオブジェクトに使うことで、ユニオン型の生成や型安全なバリデーションが可能になり、フォーム選択肢やAPIレスポンスの扱いを強力にサポートします。
`as const` を毎回書くのが煩雑な場合はユーティリティ関数で簡略化でき、Zodと組み合わせればスキーマ定義と型定義を一元化することも可能です。型の整合性を保ちながら効率的にコーディングしたいときに活用したいテクニックです。


# 参考
- [Indexed collections](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Indexed_collections)
- [TypeScript: Documentation](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html)
- [Zod](https://zod.dev/)