TypeScriptの型推論問題に学ぶ数値セパレーターとデータ型
techはじめに
みなさん、ぐんも!
今回はXで出題されていた、TypeScriptのデータ型推論問題についてTypeScript学習者向けに解説します。
この記事を読めば、
- 数値セパレーター
- リテラル型
について理解を深めることができます。
出題
const rates = {
"Node.js": 800_000,
Rust: 900_000,
} as const;
type Rate = (typeof rates)["Node.js"];
このとき、
Rateに推論される型はなんでしょうか?
-
number
-
800000
-
string
-
never
...
答えは...
800000でした!!
(この記事を書いたということはつまり、私は間違えたということです笑)
Numeric Separator(数値セパレーター)
まず、"Node.js": 800_000について...
この_が曲者です。私は知りませんでした。
結論、_は一般的には,として使われる数値を区切る記号です。
プログラミング以外の場面では、金額表記や、2進数表記などで数値を区切る場面が多々存在します。
- 300,000円
- 0001 1110(2)
このような数値の区切りを表したいとき、
const rates = {
"Node.js": 800,000,
Rust: 800 000
};
上記のように書くことはできません。
その代わりとして、_が使われています。
また、プログラム上はあくまで区切りとしての意味しか持たないため、800_000は800000として扱われます。
as const
オブジェクトや配列を完全に読み取り専用とし、リテラル型に固定するTypeScript構文です。
リテラル型
リテラル型 これはプリミティブ型の特定の値だけを代入可能にする型のことです。
今回の出題でれば、rates["Node.js"]は800000という値しか受け付けないということになります。
最もシンプルに書くならば以下のように記述します。
const isTrue: true = true;
const numelic: 345 = 345;
const greeting: "こんにちは" = "こんにちは";
そして、これをオブジェクトや配列に適用するときas constを使用します。
const fruits = {
favorite: "みかん",
soso: "りんご",
} as const;
このとき、fruits.favoriteに格納できる値はみかんのみとなります。
また、fruitsの値の型は"みかん" | "りんご"のunion型となります。
リテラル型の利点
このようにリテラル型で宣言するメリットとして以下が挙げられます。
- 定数の型定義として使用することができる
- 関数の引数などで、型・バリデーション・補完を引数だけで記述できる
具体例で見てみましょう。
リテラル型で定義することで、気づきにくいタイポを防ぐことができます。
const DIRECTION = { UP: "up", DOWN: "down" } as const;
// 1. 定数の型定義としての利用
// OK例 タイポにならない
if (direction === DIRECTION.UP) {
}
// NG例 タイポ
if (direction === "uo") {
}
リテラル型を使うことで関数を呼び出した時点で、補完はもちろん引数のバリデーション(正しいか)、データ型のチェックまで行うことができます。
const DIRECTION = { UP: "up", DOWN: "down" } as const;
// 関数 リテラル型で定義
function move(dir: "up" | "down") {
// ここに来た時点で、"up" | "down" であることが保証される
}
move(DIRECTION.UP);
// リテラル型を使わずに定義
function move(dir: string) {
// 実行時に"up" | "down"が検証される
if (dir !== "up" && dir !== "down") {
throw new Error("invalid!");
}
}
さいごに
(typeof rates)["Node.js"]はリテラル型800000として解決されます。
そのため、Rateのデータ型は800000となります。
みなさんは正解できましたか?
