TypeScript 4.9から導入された satisfies 演算子で安全に配列やオブジェクトを作ろう

はじめましての人ははじめまして。そうでないひとはお久しぶりです。猫ロキ P(@deflis/id:deflis55)です。

TypeScript 4.9 で導入された新しい演算子 satisfies をご存知でしょうか? 自分には関係ないと思っている人も多いと思うんですが、意外と使えるところが多いので、ここで紹介したいと思います。

satisfies 演算子とは?

簡単に解説します。詳しくはリリースノートを見てください。 www.typescriptlang.org

TypeScript では as const という const アサーションによって静的なオブジェクトを作ることができます。

type ColorName = 'prop1' | 'prop2';

const obj: Record<ColorName, string> = {
  prop1: 'value1',
  prop2: 'value2',
} as const;

このように、as const をつけることで、オブジェクトのプロパティが readonly になり、型安全にできるのですが、一つ問題がありました。 この例だと問題ないのですが、もう少し発展した例を見てみましょう。

type ColorName = 'red' | 'green' | 'blue';

type Color = readonly [number, number, number] | string;

const obj: Record<ColorName, Color> = {
  red: [255, 0, 0],
  green: '#00ff00',
  blue: [0, 0, 255],
} as const;

これで obj にアクセスしようとしたとき obj の中に入っている値の型が string | [number, number, number] になってしまいます。 これでは string か number の配列(タプル)か判断することができなくなります。

そこで satisfies 演算子が登場します。

type ColorName = 'red' | 'green' | 'blue';

type Color = readonly [number, number, number] | string;

const obj = {
  red: [255, 0, 0],
  green: '#00ff00',
  blue: [0, 0, 255],
} as const satisfies Record<ColorName, Color>;

このようにすると green の値だけが string でほかがタプルになっていることが型推論により分かるようになります。

どこで使えるの?

特定の値だけ入っている配列を作りたい

as constsatisfies を組み合わせることで、特定の値だけ入っている配列を作ることができます。

type ColorName = 'red' | 'green' | 'blue';

const colors = ['red', 'green', 'blue'] as const satisfies readonly ColorName[];

// これだと型推論が効かない
const colors = ['red', 'green', 'blue'] as const;

// これだと型推論が効くが、長さが不定の配列になる。一部だけ抜き出したりするときには使えない。
const colors: readonly ColorName[] = ['red', 'green', 'blue'] as const;

このようにすると colors の中には 'red' | 'green' | 'blue' しか入っていないことが型推論により分かるようになります。いままでは ColorName が入っていることが保証できないか、タプルではなく不定帳として束縛されるようになります。

type ColorName = 'red' | 'green' | 'blue';

const allowedColor = ['red', 'green'] as const satisfies readonly ColorName[];
type AllowedColor = (typeof allowedColor)[number]; // 'red' | 'green'

こういうふうにすると、allowedColor に入っている値だけを使って型を作ることもできます。

OpenAPI Generator や GraphQL Code Generator で生成された型のうちどれかを使いたい、みたいなときに役に立つのではないでしょうか。もちろん、全部使いたいときもミスしてないか保証できるので便利です。

特定のキーのオブジェクトを作りたい

これはまさにさっきの satisfies 演算子とは? で紹介した例です。

type ColorName = 'red' | 'green' | 'blue';

type Color = readonly [number, number, number] | string;

const obj = {
  red: [255, 0, 0],
  green: '#00ff00',
  blue: [0, 0, 255],
} as const satisfies Record<ColorName, Color>;

これによって、objがColorNameのキーを持ち、その値がColorであることが型推論により分かるようになります。 こんな複雑でなくても、 Record<ColorName, string> のような簡単なものでも意識的に使っていくと良いと思います。

さっきの例と組み合わせるとこんな感じの使い方ができるでしょう。

type ColorName = 'red' | 'green' | 'blue';

const allowedColor = ['red', 'green'] as const satisfies readonly ColorName[];
type AllowedColor = (typeof allowedColor)[number]; // 'red' | 'green'

const allowedColorNotation = {
  red: '赤',
  green: '緑',
} as const satisfies Record<AllowedColor, string>;

まとめ

意外とこういうところで使えるっていうのを紹介した記事がなかったので書いてみました。 使い所は他にもあると思うので活用してみてください!

それではよきTypeScriptライフを!