🛠️

satisfies を使って嬉しかったところ

2023/10/15に公開

はじめに

既存のプロダクトで、TypeScript のバージョンをアップデートし satisfies を使った実装を試してみました。
その中で、satisfies を使って特に嬉しかったところを書かせていただきます。

satisfies とは

  • TypeScript 4.9 で追加された演算子
  • 式と型がマッチしているかチェックする
  • const assertion と併用できる
  • annotation との違いとして推論結果を保持できる

詳しくは公式のドキュメントや、こちらの記事にわかりやすく紹介されています。

https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-9.html

https://zenn.dev/moneyforward/articles/typescript-as-const-satisfies

嬉しかったところ

個人的に既存のプロダクトにおいて以下のパターンで satisfies を使った実装にするととても嬉しかったです。

ある関心ごとのステータスを定義したオブジェクトと、そのバリューのユニオン型が定義されている実装がありました。(ここではポケモンの状態異常を例にさせていただいています)

type ValueOf<T, K extends keyof T = keyof T> = T[K]

const ailmentStatus = {
  Burn: 0,
  Freeze: 1,
  Paralysis: 2,
  Poison: 3,
  Sleep: 4,
  Fainting: 99,
} as const

type AilmentNumber = ValueOf<typeof ailmentStatus>
//  AilmentNumber は 0 | 1 | 2 | 3 | 4 | 99

この ailmentStatus オブジェクトには意図しない値が入ってしまう可能性があります。

const ailmentStatus = {
  Burn: 0,
  Freeze: 1,
  Paralysis: 2,
  Poison: 3,
  Sleep: 4,
  Fainting: 99,
  Terastal: 10000, // 意図していない Terastal が入ってしまう
} as const

Terastal のような意図しない値が入ってしまわないように annotation を使って実装すると以下のようになります。

type Ailment = 'Burn' | 'Freeze' | 'Paralysis' | 'Poison' | 'Sleep' | 'Fainting'

type AilmentStatus = {
  [key in Ailment]: number
}

const ailmentStatus: AilmentStatus = {
  Burn: 0,
  Freeze: 1,
  Paralysis: 2,
  Poison: 3,
  Sleep: 4,
  Fainting: 99,
  Terastal: 10000, // 'Terastal' は型 'AilmentStatus' に存在しません。
} as const

type AilmentNumber = ValueOf<typeof ailmentStatus>
//  AilmentNumber は number

こうすることによって型エラーが出るようになり、意図しない値が入ることを防ぐことが可能になりました。

しかし、当たり前ですが AilmentStatus 型でアノテーションすると、AilmentNumber が number 型になってしまいます。

この時以下のように satisfies を使うことで求めていたことができます。

const ailmentStatus = {
  Burn: 0,
  Freeze: 1,
  Paralysis: 2,
  Poison: 3,
  Sleep: 4,
  Fainting: 99,
  Terastal: 10000, // 'Terastal' は型 'AilmentStatus' に存在しません。
} as const satisfies AilmentStatus

type AilmentNumber = ValueOf<typeof ailmentStatus>
// AilmentNumber は number 型ではなく、ailmentStatus のバリューのユニオン型になる

satisfies によって型のチェックを行いつつ、推論結果を使って AilmentNumber 型も ailmentStatus のバリューのユニオン型のまま使うことができます。

まとめ

個人的に既存のプロダクトで satisfies を使って嬉しかったところを書かせていただきました。

他にもまだまだあるかもしれませんので、もう少し深ぼってキャッチアップしていこうと思いました。

参考

GitHubで編集を提案

Discussion