0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【TypeScript】satisfies演算子を使って型安全性UP!

0
Posted at

概要

satisfies

  • 型推論の結果を残したままで
  • 特定の型を満たしているか

をチェックすることができる演算子。

特定の型を満たしているかの確認

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>;

変数paletteは、satisfies Record<Colors, string | RGB>という記述により、以下の条件を満たさなくてはならなくなった。

  • プロパティ名がColors型のキーをすべて持つ必要がある
  • 各プロパティの値はstringRGB型を満たすようなものである

これを満たさない場合はエラーになる。今回の例では、プロパティbleuColors型に存在しないので、下の画像のようなエラーが表示される。

image.png

satisfiesを使うことで、タイポを検出することに成功した!

型推論の結果を保つ

ところで、先ほどのタイポ検出をしたいだけなら、satisfiesを使う必要はない。型注釈(アノテーション)でも事足りる。

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

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

// paletteに型注釈を追加
const palette: Record<Colors, string | RGB> = {
	red: [255, 0, 0],
	green: "#00ff00",
	bleu: [0, 0, 255],
};

このように書いたとしても同様のエラーが表示される。ただ、型注釈だと以下のようなことができない。

const greenNormalized = palette.green.toUpperCase();

toUpperCase()の部分に、下の画像のようなエラーが表示される。

image.png

これはプロパティgreenの値がstring | RGB型であると推論されていることが原因である。toUpperCase()はstring値が持つメソッドであり、もしプロパティgreenの値がRGB型だったら、toUpperCase()を呼び出すことはできない。なので、エラーになっている。

一方、satisfiesを使うことで、このエラーを回避することができる。

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>;

// toUpperCase() でエラーが出ない!
const greenNormalized = palette.green.toUpperCase();

これはsatisfiesが、型推論の結果を残しつつ指定した型を満たすかの確認だけを行うからである。

型注釈の時とsatisfiesを使った時、paletteがどのような型であると推論されるか、確認してみる。

型注釈を使った場合、当然だが型注釈の内容に推論されている。配列や文字列など、paletteの各プロパティが持つ具体的な値についての情報は失われてしまい、値はstring | RGB型である、というふうにまとめられてしまっている。

image.png

一方satisfiesを使った場合、型推論の結果が異なっており、実際の値から推論された結果が残っていることがわかる。

image.png

satisfiesを使った時にtoUpperCase()でエラーにならなかったのは、プロパティgreenの値がstring | RGB型ではなく、string型と推論されたからである!

このように、satisfiesには型推論の結果を残すことができる、というメリットがある。

as constとの組み合わせ

satisfiesas constと組み合わせて使うことがある。

as constを使うと、型のwideningを防ぐことができる。wideningとは、型推論の時に、より広い型に推論がされることである。

// foods1の型は string[]
const foods1 = ["rice", "curry", "apple"];

// foods2の型はreadonlyの["rice", "curry", "apple"]型
const foods2 = ["rice", "curry", "apple"] as const;

上記の例だと、foods1string[]型に推論される。これはwideningの典型的な例である。

一方as constがついているfoods2は、配列の要素の値も数も決まっており、readonlyがついて読み取り専用になっている。型推論の結果が、実際の値を厳格に反映したものになっていることがわかる。
絶対に定数として扱いたい、最初に定義した値以外には絶対したくない、というときにas constがとても有用である。

satisfiesas constと組み合わせて使うことで

  • 必ず特定の型を満たしている
  • 型推論を厳格にする

というメリットが得られる。

最初の例ではpaletteを1つだけ定義していたが、ライトモードとダークモードでそれぞれ異なる色を使いたくなったと仮定し、コードを修正してみる。

type Theme = "light" | "dark";
type Palette = {
	primary: string;
	background: string;
	text: string;
};

const PALETTE = {
	light: {
		primary: "#2563eb",
		background: "#f8fafc",
		text: "#0f172a",
	},
	dark: {
		primary: "#60a5fa",
		background: "#0b1220",
		text: "#e2e8f0",
	},
} as const satisfies Record<Theme, Palette>;

satisfies Record<Theme, Palette>があるおかげで、lightdarkPalette型にないプロパティがあったり、逆に余分なプロパティがあるときにエラーが表示される。
これにより、lightにあるものは必ずdarkにもあるので、PALETTEをインポートする側は非常に安心である。

またas constがあるおかげで、PALETTEは変更不可の値であり、どこから呼び出しても同じ値を返してくれることが保証される。

このように、as const satisfiesは定数を定義するときに、型安全性を向上させてくれる。

参考

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?