satisfies 演算子でできること
satisfies は TypeScript 4.9 で追加された演算子だ。型アノテーションとは異なり、値の型を維持しつつ、特定の型に適合するかをチェックできる。従来の型アノテーションでは実現できなかった柔軟な型付けが可能になる。
型アノテーションの限界
まず、従来の型アノテーションの問題点を見てみよう。
type Colors = Record<string, string>;
const colors: Colors = {
red: "#ff0000",
green: "#00ff00",
blue: "#0000ff",
};
// colors.red は string 型
// colors.purple も string 型(存在しないのにエラーにならない)
const red = colors.red; // string
const purple = colors.purple; // string(実行時は undefined)Record<string, string> と型を指定すると、任意のキーでアクセスできてしまう。存在しないキーへのアクセスもエラーにならない。
satisfies の登場
satisfies を使うと、型チェックを行いつつ、より具体的な型を維持できる。
type Colors = Record<string, string>;
const colors = {
red: "#ff0000",
green: "#00ff00",
blue: "#0000ff",
} satisfies Colors;
// colors.red は "#ff0000" 型(リテラル型)
// colors.purple はエラー
const red = colors.red; // "#ff0000"
// const purple = colors.purple; // エラー: Property 'purple' does not existsatisfies は「この値は Colors 型の条件を満たすが、推論された型をそのまま使う」という意味だ。
指定した型に強制される。推論された型情報は失われる。
指定した型に適合するかチェックされる。推論された型は維持される。
よくあるユースケース
設定オブジェクトの型チェック
type Config = {
port: number;
host: string;
debug: boolean;
};
const config = {
port: 3000,
host: "localhost",
debug: true,
} satisfies Config;
// config.port は 3000(リテラル型)
// 型アノテーションだと number になる許可された値のチェック
type Theme = "light" | "dark" | "system";
const themeConfig = {
default: "light",
current: "dark",
} satisfies Record<string, Theme>;
// default と current が Theme 型であることを保証
// かつ、themeConfig.default は "light" 型として推論される関数のオブジェクト
type EventHandlers = Record<string, (...args: any[]) => void>;
const handlers = {
onClick: (e: MouseEvent) => console.log(e),
onKeyDown: (e: KeyboardEvent) => console.log(e),
} satisfies EventHandlers;
// handlers.onClick は (e: MouseEvent) => void として推論される
// 型アノテーションだと (...args: any[]) => void になってしまうas const との組み合わせ
satisfies と as const を組み合わせると、さらに強力になる。
const routes = {
home: "/",
about: "/about",
contact: "/contact",
} as const satisfies Record<string, string>;
// routes.home は "/" 型(リテラル型かつ readonly)
// 型チェックも行われるas const で完全に readonly なリテラル型にしつつ、satisfies で構造のチェックを行う。
リテラル型として固定されるが、型チェックは行われない。タイポがあっても検出できない。
型チェックは行われるが、as const ほど厳密なリテラル型にはならない。
リテラル型として固定され、かつ型チェックも行われる。最も厳密。
エラー検出の例
satisfies は値の構造が型に適合しない場合、具体的なエラーを出す。
type Config = {
port: number;
host: string;
};
const config = {
port: "3000", // エラー: Type 'string' is not assignable to type 'number'
host: "localhost",
} satisfies Config;型アノテーションでも同じエラーは出るが、satisfies を使うと推論される型が維持される点が異なる。
注意点
satisfies はチェックのみを行い、型を変換しない。
const value = "hello" satisfies string;
// value の型は "hello"(リテラル型)
const value2: string = "hello";
// value2 の型は string型を広げたい場合は型アノテーションを使い、推論を維持しつつチェックしたい場合は satisfies を使う。目的に応じて使い分けよう。
satisfies は比較的新しい機能だが、設定オブジェクトやルーティング定義など、「構造は決まっているが具体的な値も保持したい」場面で威力を発揮する。積極的に活用していきたい。