結論
ケースによって型アノテーションとas const satisfiesを使い分けましょう
現状の雑な理解:
- プリミティブ型の定数やオブジェクトリテラルはas const satisfiesで
- それ以外は型アノテーション
型推論の場合
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]
}
// 型アノテーションや型アサーションがなければTypeScriptのコンパイラがpalette変数の型を推論してくれる
const redComponent = palette.red
// -> redはnumber[]で推論される
// なのでインデックス外へのアクセスがコンパイルエラーにならない 😭
const outOfIndex = palette.red[3]
// -> undefined
const greenComponent = palette.green
// -> greenはstringに推論される
// なのでstringのプロパティの使用がコンパイルエラーにならない 😁
const upperCase = palette.green.toUpperCase();
// -> "#00FF00"
型アノテーションの場合
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",
blue: [0, 0, 255]
}
const redComponent = palette.red
// -> redはstring | RGB に推論される
// なのでインデックス外へのアクセスがコンパイルエラーにならない 😭
const outOfIndex = palette.red[3]
// -> undefined
const greenComponent = palette.green
// -> greenはstring | RGB に推論される
// なのでstringのプロパティの使用がコンパイルエラーになる 😭
const upperCase = palette.green.toUpperCase();
// ^^^^^^^^^^^^^^^^
satisfies演算子の場合
type Colors = "red" | "green" | "blue";
type RGB = [number, number, number];
const palette = {
red: [255, 0, 0],
green: "#00ff00",
blue: [0, 0, 255]
} satisfies Record<Colors, string | RGB>;
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ こういうやつ
const redComponent = palette.red
// -> redは[number, number, number]で推論される
// なのでインデックス外へのアクセスがコンパイルエラー 😁
const outOfIndex = palette.red[3]
// ^^^
const greenComponent = palette.green
// -> greenはstringに推論される
// なのでstringのプロパティの使用がコンパイルエラーにならない 😁
const upperCase = palette.green.toUpperCase();
// -> "#00FF00"
補足
定数として使う場合にはsatisfies
単独よりもas const satisfies
で使った方がimportしたファイル側でwideningしなくてよいらしい