TypeScriptのsatisfiesがすごい便利！ということなので時間を取ってじっくり学習してみた

株式会社ココロファン - エンジニアリング事業部所属の Sassy です。

最近は人材マネジメントに興味があり、社員の自主性を高め効率的な育成を模索しています。

去年の11月頃にリリースされたTypeScript4.9に入っている **satisfies**（サティスファイズ）というオペレータがあります。

私はまだ使った方が良いケースに遭遇してないので使用してませんが、これがすごい便利そうなので改めて勉強しようと思います！

https://devblogs.microsoft.com/typescript/announcing-typescript-4-9/

> TypeScript developers are often faced with a dilemma: we want to ensure that some expression matches some type, but also want to keep the most specific type of that expression for inference purposes.
> 
> 出典：https://devblogs.microsoft.com/typescript/announcing-typescript-4-9/#the-satisfies-operator

> TypeScriptの開発者はしばしばジレンマに直面する。ある式がある型にマッチすることを保証したいが、推論のためにその式の最も具体的な型も残しておきたいのだ。

ではsatisfiesオペレータについて、サイトに掲載されていたサンプルコードをベースに見ていきます。

```typescript
type Colors = "red" | "green" | "blue";

type RGB = [red: number, green: number, blue: number];

const palette: Record<Colors, string | RGB> = {
    red: [255, 0, 0],
    green: "#00ff00",
    bleu: [0, 0, 255]
};

const redComponent = palette.red.at(0);
```

`bleu`というプロパティ名のタイポを検出するために、プロパティ名を定義した`Colors`という型を用意してRecordの型パラメータに渡しています。

またValueの部分を`string | RGB`として型を渡していますね。

こうすることで`palette`のオブジェクトを定義する際にプロパティ名に`Colors`で指定された以外のプロパティ名を書いたときに`Colors`に含まれていないのでコンパイル時に型エラーを検出できるようになるということです。

しかし、これだと確かに`palette.red.at(0)`と書くことは出来ないですね。

`string | RGB`と書いていることで`red`は`string | RGB`と推論され、`string`である可能性も含まれているからです。

これを解決するにはプロパティが`string`または`RGB`の型であるかを判定する必要がありそうですが細かい実装が増えてしまいますね。

サンプルコードでは
```typescript
{
  red: string | RGB;
  green: string | RGB;
  blue: string | RGB;
}

```
と推論されていますが、この場合以下のように推論されてほしいと感じるはずです。
```typescript
{
  red: RGB;
  green: string;
  blue: RGB;
}
```
と推論されてほしいはずです。

この問題を解決してくれるのが `satisfies` オペレータです。

`palette`に付けた`Record<Colors, string | RGB>`という型アノテーションを削除して、`parette`オブジェクトの最後に`satisfies Record<Colors, string | RGB>`と付けてみましょう。

```typescript
type Colors = "red" | "green" | "blue";

type RGB = [red: number, green: number, blue: number];

const palette = {
    red: [255, 0, 0],
    green: "#00ff00",
    bleu: [0, 0, 255]
} satisfies Record<Colors, string | RGB>;

const redComponent = palette.red.at(0);
const greenNormalized = palette.green.toUpperCase();
```

`satisfies`を使うことで個々のプロパティの詳細な型情報を保持して使用する時に具体的な型を推論することができるようになりました。

`palette.red`を見てみると`[number, number, number]`となっており、`green`を見てみると`string`となっているはずです。

Valueの詳細な型情報が保持されています。

またオブジェクトがある方のキーを全て持っていて、それ以上持っていないことだけを確認したい場合は`satisfies Record<Colors,unknown>`とすればよいです。

`Record`の型パラメータの2番目の型に`unknown`を渡して型を緩めてあげます。

```typescript
const palette = {
    red: [255, 0, 0],
    green: "#00ff00",
    bleu: [0, 0, 255]
} satisfies Record<Colors,unknown>;
```

逆に値の型のみをチェックできるようにしたい場合には`satisfies Record<string, string | RGB>`として、以下の様に書けば検出できます。

`Record`の型パラメータの1番目に`string`を渡して型を緩めてあげます。

```typescript
type Colors = "red" | "green" | "blue";

type RGB = [red: number, green: number, blue: number];

const palette = {
    red: [255, 0, 0],
    green: "#00ff00",
    blue: [0, 0, 255],
    yellow: [0, 0]
} satisfies Record<string, string | RGB>;
```

その他は、定数のオブジェクトを作成したときなどにも便利です。

`as const satisfies`とすることでWideningを防ぎつつ型チェックも可能にしてくれます。

```typescript
type Colors = "red" | "green" | "blue";

export const palette = {
    red: "#ff0000",
    green: "#00ff00",
    blue: "#0000ff",
    yellow: "satisfies"
} as const satisfies Record<Colors, unknown>
```

TypeScriptをかっちり使っている人にとってはかゆいところに手が届くオペレータなので、すごい便利！だなと感じました。

今後使えそうなケースがあれば積極的に使っていきたいと思います。

## 最後に

株式会社ココロファンでは受託や自社プロダクトの開発も行なっており、やってよかったこと、やってわかったこと、便利なノウハウを皆さんに共有できるように、これからも積極的に技術記事をアップしていきたいと思います。

https://cocolofun.co.jp/