株式会社ココロファン - エンジニアリング事業部所属の 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をかっちり使っている人にとってはかゆいところに手が届くオペレータなので、すごい便利!だなと感じました。
今後使えそうなケースがあれば積極的に使っていきたいと思います。
最後に
株式会社ココロファンでは受託や自社プロダクトの開発も行なっており、やってよかったこと、やってわかったこと、便利なノウハウを皆さんに共有できるように、これからも積極的に技術記事をアップしていきたいと思います。