2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

はじめに

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

satisfies とは?

TypeScriptの satisfies 演算子は、値が指定された型を満たすことを確認しながら、元の型推論を保持するためのキーワードです。

従来の型注釈(:)や型アサーション(as)と異なり、satisfies は型の制約を確認しつつ、TypeScriptの型推論による具体的な型情報を失わないという特徴があります。

他の型機能との違い

satisfies vs 型推論

  • 型推論のみ: TypeScriptが自動的に型を推測するが、型制約のチェックは行わない
  • satisfies: 型の制約を確認しつつ、具体的な型推論を保持
type Config = {
  theme: "light" | "dark";
  size: "small" | "medium" | "large";
};

// 型推論のみ
const config1 = {
  theme: "light",
  size: "medium"
};
// 推論: { theme: string; size: string }(型制約なし、幅広い型)

// satisfies を使用
const config2 = {
  theme: "light",
  size: "medium"
} satisfies Config;
// 推論: { theme: "light"; size: "medium" }(型制約あり、具体的な値の型が保持)

// 型推論だけだと、不正な値でもエラーにならない
const invalidConfig1 = {
  theme: "blue", // ❌ 実際は無効だが、エラーにならない
  size: "huge"   // ❌ 実際は無効だが、エラーにならない
};

// satisfies なら不正な値でエラーになる
const invalidConfig2 = {
  theme: "blue", // ❌ エラー: '"blue"' は型 '"light" | "dark"' に割り当てできません
  size: "huge"   // ❌ エラー: '"huge"' は型 '"small" | "medium" | "large"' に割り当てできません
} satisfies Config;

satisfies vs 型注釈(:

  • 型注釈(:: 変数の型を明示的に指定し、具体的な型情報は失われる
  • satisfies: 型の制約を確認しつつ、具体的な型推論を保持
type Config = {
  theme: "light" | "dark";
  size: "small" | "medium" | "large";
};

// 型注釈を使用
const config1: Config = {
  theme: "light",
  size: "medium"
};
// 推論: Config(具体的な "light", "medium" の情報が失われる)

// satisfies を使用
const config2 = {
  theme: "light",
  size: "medium"
} satisfies Config;
// 推論: { theme: "light"; size: "medium" }(具体的な値の型が保持される)

satisfies vs 型アサーション(as

  • 型アサーション(as: 型を強制的に変更(型安全性が損なわれる可能性)
  • satisfies: 型の制約を確認(型安全性を保持)
type Config = {
  theme: "light" | "dark";
  size: "small" | "medium" | "large";
};

// 型アサーションを使用
const config1 = {
  theme: "light",
  size: "medium"
} as Config;
// config1 の型: Config(具体的な型情報が失われる)

// satisfies を使用
const config2 = {
  theme: "light",
  size: "medium"
} satisfies Config;
// config2 の型: { theme: "light"; size: "medium" }(具体的な値の型が保持される)

// 型アサーションの危険性
const dangerousConfig = {
  theme: "light",
  size: "medium",
  invalidProp: "value" // 余分なプロパティ
} as Config; // ❌ エラーにならない!型安全性が損なわれる

const safeConfig = {
  theme: "light",
  size: "medium",
  invalidProp: "value" // 余分なプロパティ
} satisfies Config; // ❌ エラー: オブジェクトリテラルは既知のプロパティのみ指定できます

主な用途

satisfies 演算子は、型安全性を保ちながら、具体的な型情報を活用したい場合に便利です。必須プロパティの漏れやタイポを防ぎつつ、型推論による具体的な値の型を保持したい実用的な設定管理で有効です。

type ApiConfig = {
  endpoints: {
    users: string;
    posts: string;
    comments: string;
  };
  timeouts: {
    default: number;
    upload: number;
    download: number;
  };
  retryPolicy: {
    maxRetries: number;
    backoffMs: number;
  };
};

// ❌ satisfies なしの場合 - 必須プロパティが漏れても気づかない
const unsafeConfig = {
  endpoints: {
    users: "/api/users",
    posts: "/api/posts"
    // comments が漏れている!
  },
  timeouts: {
    default: 5000,
    upload: 30000
    // download が漏れている!
  },
  retryPolicy: {
    maxRetries: 3,
    backoffMs: 1000
  }
};

// ✅ satisfies ありの場合 - 必須プロパティの漏れでコンパイルエラー
const safeConfig = {
  endpoints: {
    users: "/api/users",
    posts: "/api/posts",
    comments: "/api/comments"
  },
  timeouts: {
    default: 5000,
    upload: 30000,
    download: 10000
  },
  retryPolicy: {
    maxRetries: 3,
    backoffMs: 1000,
  },
} satisfies ApiConfig;

メリット

  • 必須プロパティの漏れを防ぐ
  • タイポによるエラーを防ぐ
  • 具体的な値の型推論を保持

as const との組み合わせによるリテラル型推論

satisfiesas const を組み合わせることで、型制約を確認しながら具体的な値をリテラル型として推論させることができます。

type Config = {
  port: number;
  host: string;
  ssl: boolean;
};

// satisfies のみの場合
const config1 = {
  port: 3000,
  host: "localhost",
  ssl: false
} satisfies Config;
// config1.port の型は number
// config1.host の型は string
// config1.ssl の型は boolean

// as const と satisfies の組み合わせ
const config2 = {
  port: 3000,
  host: "localhost", 
  ssl: false
} as const satisfies Config;
// config2.port の型は 3000(リテラル型)
// config2.host の型は "localhost"(リテラル型)
// config2.ssl の型は false(リテラル型)

// 型制約違反があった場合はエラーになる
const invalidConfig = {
  port: 3000,
  host: "localhost",
  ssl: "invalid" // ❌ エラー: boolean が期待されるのに string が指定されている
} as const satisfies Config;

この組み合わせは、設定オブジェクトや定数定義において特に有用で、型安全性を保ちながら完全にリテラル型として推論させることができます。

注意点

satisfies 演算子を使用する際の注意点をいくつか紹介します。

1. 型の制約が厳格

satisfies は指定された型の制約を厳格にチェックするため、型アサーション(as)との比較で言及したように余分なプロパティがあるとエラーになります。

type Person = {
  name: string;
  age: number;
};

const person = {
  name: "Alice",
  age: 25,
  city: "Tokyo" // 余分なプロパティ
} satisfies Person; // ❌ エラー: オブジェクトリテラルは既知のプロパティのみ指定できます

2. TypeScript 4.9以降が必要

satisfies 演算子は TypeScript 4.9 で導入された機能のため、それ以前のバージョンでは使用できません。

3. 実行時の型チェックは行わない

satisfies はコンパイル時の型チェックのみを行い、実行時の型チェックは行いません。

type Config = {
  port: number;
  host: string;
};

// コンパイル時は問題なし
const config = {
  port: 3000,
  host: "localhost"
} satisfies Config;

// 実行時に値を変更しても型チェックは行われない
(config as any).port = "invalid"; // 実行時エラーにはならない

まとめ

satisfies 演算子は、TypeScript 4.9で導入された画期的な機能であり、「厳格な型制約のチェック」と「詳細な型推論の保持」という、これまで両立が難しかった要素を同時に実現します。

従来のTypeScriptでは、以下のいずれかの選択を迫られることがありました。

  • 型推論に任せる: コードの記述は簡潔になるが、意図しない値が代入されてもコンパイル時、エラーにならないなど、型安全性が損なわれる
  • 型注釈(:)を使用する: 型安全性を確保できるが、値が持つ具体的なリテラル型などの情報が失われ、その後のコードで詳細な型に基づく処理を行う場合に不便が生じる
  • 型アサーション(as)を使用する: 強制的に型を適用するため、誤った型をアサートすると型安全性が大きく損なわれる

satisfies 演算子を用いることで、これらの課題が解決されます。例えば、設定オブジェクトのように「特定の構造を持つが、個々の値は具体的なリテラル型として扱いたい」といった場面で特に威力を発揮します。

本記事で解説したように、satisfies は必須プロパティの漏れやタイポを防ぎつつ、各プロパティの値を最も具体的な型(リテラル型など)として推論させることができます。さらに、as const と組み合わせることで、オブジェクト全体を不変のリテラル型として扱うことも可能です。

satisfies を適切に活用することで、より安全で、かつ柔軟性の高いTypeScriptコードを記述できるようになり、結果として開発効率と保守性の向上が期待できます。

参考

関連記事

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?