| name | as-const-satisfies |
| description | Use `as const` and `as const satisfies Type` for constant definitions in TypeScript. Use when defining constants, config objects, or string union types. |
| user-invocable | false |
as const satisfies パターンの使用
基本原則
定数定義ではas constを積極的に使用します。
as constによりリテラル型が保持され、
より厳密な型推論が可能になります。
名前のついている特定の型で型チェックも出来る場合はas const satisfies Typeを使用します。
これで「as constを使いたいが、型のチェックもしたい」という要求を満たせます。
使い分け
as constを使う場合
定数定義では基本的にas constを使います。
リテラル型の保持と型レベルでのreadonly(ただし実行時にはオブジェクトは凍結されません)が得られます。
const status = {
pending: "pending",
approved: "approved",
rejected: "rejected",
} as const;
as const satisfies Typeを使う場合
as constの利点を保ちつつ型チェックも行いたい場合に使用します。
const status = {
pending: "pending",
approved: "approved",
rejected: "rejected",
} as const satisfies Record<string, string>;
: Type や satisfies Type を使う場合
可変性が必要な場合や、
リテラル型の保持が問題になる場合に限り使用します。
const items: string[] = [];
items.push("a");
as const satisfiesがそのまま使えないパターン
satisfiesで指定する型が可変な型の場合、
as constのreadonly性と競合してエラーになることがあります。
問題のあるパターン
type Config = {
values: string[];
};
const config = {
values: ["a", "b", "c"],
} as const satisfies Config;
解決方法: readonlyな型を定義する
satisfiesで使用する型自体をreadonlyにすることで解決できます。
type Config = {
readonly values: readonly string[];
};
const config = {
values: ["a", "b", "c"],
} as const satisfies Config;
Readonlyユーティリティ型の活用
既存の型をreadonly化する場合はReadonlyや再帰的なDeepReadonlyを使用できます。
type MutableConfig = {
values: string[];
nested: {
items: number[];
};
};
type ShallowReadonlyConfig = Readonly<MutableConfig>;
type DeepReadonly<T> = T extends readonly (infer U)[]
? readonly DeepReadonly<U>[]
: T extends object
? { readonly [P in keyof T]: DeepReadonly<T[P]> }
: T;
type ReadonlyConfig = DeepReadonly<MutableConfig>;
const config = {
values: ["a", "b", "c"],
nested: {
items: [1, 2, 3],
},
} as const satisfies ReadonlyConfig;
配列型のreadonly化
配列を含む型ではreadonly T[]またはReadonlyArray<T>を使用する。
type Items = readonly string[];
type Items2 = ReadonlyArray<string>;
const items = ["a", "b", "c"] as const satisfies readonly string[];
型のreadonly化があまりにも複雑になる場合
DeepReadonlyやビルトインオブジェクトの対応など、
as const satisfiesのために型側の調整が複雑になりすぎる場合は、
無理にas constにこだわらず、
通常の型推論や型ヒントで済ませて構いません。
リテラル型のために可読性を大きく犠牲にする必要はありません。
文字列ユニオン型はas const配列から導きます
文字列ユニオン型を定義するときは、
まずas const配列を作り、
そこから型を導きます。
直接type Foo = "a" | "b" | "c"とは書きません。
const fooValues = ["a", "b", "c"] as const;
type Foo = (typeof fooValues)[number];
type Foo = "a" | "b" | "c";
配列として値を持つことで、
ランタイムでも値の列挙が必要な場合(バリデーション等)に再利用できます。
特にincludesで型を絞り込みやすいのがメリットです。
現時点でランタイムの列挙が不要な場合でも、
一貫性のためにas const配列から導くスタイルに統一してください。
後からランタイムでも値が必要になったときに書き換えが発生しませんし、
コードベース内で2つのスタイルが混在するのを防げます。