株式会社ココロファン - エンジニアリング事業部所属の Sassy です。
最近は人材マネジメントに興味があり、社員の自主性を高め効率的な育成を模索しています。
去年の11月頃にリリースされたTypeScript4.9に入っている satisfies(サティスファイズ)というオペレータがあります。
私はまだ使った方が良いケースに遭遇してないので使用してませんが、これがすごい便利そうなので改めて勉強しようと思います!
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オペレータについて、サイトに掲載されていたサンプルコードをベースに見ていきます。
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
の型であるかを判定する必要がありそうですが細かい実装が増えてしまいますね。
サンプルコードでは
{
red: string | RGB;
green: string | RGB;
blue: string | RGB;
}
と推論されていますが、この場合以下のように推論されてほしいと感じるはずです。
{
red: RGB;
green: string;
blue: RGB;
}
と推論されてほしいはずです。
この問題を解決してくれるのが satisfies
オペレータです。
palette
に付けたRecord<Colors, string | RGB>
という型アノテーションを削除して、parette
オブジェクトの最後にsatisfies Record<Colors, string | RGB>
と付けてみましょう。
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
を渡して型を緩めてあげます。
const palette = {
red: [255, 0, 0],
green: "#00ff00",
bleu: [0, 0, 255]
} satisfies Record<Colors,unknown>;
逆に値の型のみをチェックできるようにしたい場合にはsatisfies Record<string, string | RGB>
として、以下の様に書けば検出できます。
Record
の型パラメータの1番目にstring
を渡して型を緩めてあげます。
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を防ぎつつ型チェックも可能にしてくれます。
type Colors = "red" | "green" | "blue";
export const palette = {
red: "#ff0000",
green: "#00ff00",
blue: "#0000ff",
yellow: "satisfies"
} as const satisfies Record<Colors, unknown>
TypeScriptをかっちり使っている人にとってはかゆいところに手が届くオペレータなので、すごい便利!だなと感じました。
今後使えそうなケースがあれば積極的に使っていきたいと思います。
最後に
株式会社ココロファンでは受託や自社プロダクトの開発も行なっており、やってよかったこと、やってわかったこと、便利なノウハウを皆さんに共有できるように、これからも積極的に技術記事をアップしていきたいと思います。