TypeScriptを使っていて、型チェックを通したいけれど、変数の型を明示的に指定しすぎると、エディタの自動補完が効かなくなる経験をしたことはありませんか。
特に複雑なオブジェクト型やAPI レスポンスを扱うときに、as演算子を多用していると、型安全性が失われる不安感に駆られることもあるでしょう。そこで登場するのが、TypeScript 4.9で導入されたsatisfies演算子です。
この記事では、satisfies演算子とas演算子の違いを実務的に理解し、型安全性を保ちながら開発効率を高める方法をステップバイステップで学びます。
satisfies演算子とは何か
satisfies演算子は、変数が特定の型を満たしているかチェックしながらも、変数の本来の型を保持する機能です。
簡単に言えば、「この値は◯◯型の要件を満たしていますか」と型チェッカーに確認させつつ、変数自体の型推論は維持するということです。
具体的なコード例を見てみましょう。
const user = {
name: "田中太郎",
age: 30,
email: "tanaka@example.com"
} satisfies { name: string; age: number };
// userの型は { name: string; age: string; email: string } のまま
// email プロパティへのアクセスも可能
この例では、userオブジェクトが{ name: string; age: number }を満たしているかチェックされますが、userの型は元の推論型を保つため、emailプロパティにもアクセスできます。
satisfies演算子と as 演算子の違いを比較
多くのエンジニアがasとsatisfiesの区別に悩みます。実際のコード例で両者の違いを明確にしましょう。
| 項目 | as演算子 | satisfies演算子 |
|---|---|---|
| 型チェック | チェックなし。開発者の判断に任せる | 型チェックあり。要件を満たしているか確認 |
| 型推論の保持 | 指定した型に上書きされる | 元の推論型を保持 |
| 自動補完 | 指定型のメンバーのみ補完 | 元の型のすべてのメンバーで補完 |
| 実行時エラー | 型チェック時にエラーが出ず、実行時に不具合が生じる可能性 | コンパイル時に型エラーが検出される |
| 使用場面 | 型キャスト、移行期のコード | 型安全性を確保しながら柔軟な推論を保つ場面 |
as演算子の問題点を理解する
まず、従来のas演算子がなぜ問題を起こしやすいのかを見てみましょう。
// as を使った場合
const config = {
apiUrl: "https://api.example.com",
timeout: 5000,
retryCount: 3
} as { apiUrl: string; timeout: number };
// このコード自体はコンパイルエラーなし
// しかし config.retryCount にアクセスしようとするとエラー
// エディタも retryCount を補完してくれない
asで型を指定すると、指定した型に完全に上書きされます。つまり、retryCountプロパティは型から消えてしまい、後で参照しようとするとエラーになるのです。
さらに危険な例もあります。
// 誤った型指定をしても、as は警告しない
const value = "hello" as number; // 本来なら型エラーになるべき
// 実行時までエラーが発見されない
satisfies演算子の実践的な使い方
それでは、satisfies演算子を正しく活用する方法を、実務的な例で学びましょう。
ステップ 1:基本的な使用例
type UserConfig = {
name: string;
age: number;
};
const user = {
name: "田中太郎",
age: 30,
role: "admin" // 追加のプロパティ
} satisfies UserConfig;
// user の型は { name: string; age: number; role: string } として推論される
// role プロパティにもアクセス可能
console.log(user.role); // "admin"
このステップで理解すべきポイントは、satisfiesでUserConfigの要件をチェックしながらも、オブジェクト本来の型情報は失われないということです。
ステップ 2:API レスポンスの型安全性を確保する
実務でよくある例として、APIレスポンスの扱いを見てみます。
type ApiResponse = {
status: "success" | "error";
data?: unknown;
};
const response = {
status: "success",
data: {
userId: 123,
userName: "tanaka"
},
timestamp: new Date()
} satisfies ApiResponse;
// response.data にアクセスすると、型は推論される
// TypeScript は timestamp プロパティも認識している
const userId = response.data?.userId; // 型安全
このようにすることで、APIレスポンスがApiResponse型の要件を満たしていることを保証しながら、実際のデータ構造も活かせます。
ステップ 3:オブジェクトの値の型チェック
設定ファイルやマッピングテーブルの値の型をチェックする場面は多いです。
type StatusColor = {
[key: string]: "red" | "yellow" | "green";
};
const statusMap = {
pending: "yellow",
completed: "green",
failed: "red",
processing: "yellow"
} satisfies StatusColor;
// statusMap の keys は推論される
// エディタは自動補完でpending, completed などを提案してくれる
const color = statusMap.pending; // "yellow" | "yellow" | "green" | "red"
satisfiesを使うことで、値の型チェックとキー情報の保持を両立させられます。
ステップ 4:複雑なオブジェクトの実践例
より実務的な例として、フォーム定義オブジェクトを考えてみましょう。
type FormField = {
label: string;
type: "text" | "email" | "number";
required: boolean;
};
const loginForm = {
email: {
label: "メールアドレス",
type: "email",
required: true,
placeholder: "user@example.com" // 追加プロパティ
},
password: {
label: "パスワード",
type: "text",
required: true,
minLength: 8 // 追加プロパティ
}
} satisfies Record;
// loginForm の各フィールドの追加プロパティにアクセスできる
console.log(loginForm.email.placeholder); // "user@example.com"
console.log(loginForm.password.minLength); // 8
Record<string, FormField>との組み合わせで、キー名を保持しながら各フィールドの型安全性を確保しています。
satisfies演算子を実務で活用するベストプラクティス
ライブラリ連携時の型チェック
外部ライブラリを使う際、設定オブジェクトが正しい型で渡されているか確認する場合に有効です。
import type { Config } from "some-library";
const myConfig = {
debug: true,
timeout: 30000,
logLevel: "info",
customOption: "value"
} satisfies Config;
// myConfig は元の型推論を保つため、customOption にもアクセス可能
// 同時に Config 型の要件をコンパイル時にチェックしている
イミュータブルなデータの定義
定数としてのデータ定義で、satisfiesは特に活躍します。
const PERMISSION_RULES = {
admin: {
canRead: true,
canWrite: true,
canDelete: true
},
user: {
canRead: true,
canWrite: false,
canDelete: false
}
} satisfies Record;
// PERMISSION_RULES.admin.canDelete など、すべてのプロパティに正確にアクセス可能
satisfies演算子を導入する際の注意点
satisfiesは非常に便利ですが、いくつか注意すべき点があります。
- TypeScript 4.9以上が必須です。プロジェクトのバージョン確認をしましょう
- チーム内で satisfies と as の使い分けルールを決めておくと、コード品質が向上します
- 複雑なジェネリクスと組み合わせる場合、型推論が予想外になることもあるため、テストが重要です
- レガシーコードの移行時は、as から satisfies への段階的な置き換えを検討してください
satisfies演算子と他の型安全性ツールの組み合わせ
satisfiesの効果は、他のツールとの組み合わせでさらに高まります。pytest モック 使い方|外