Node.jsとDeno界隈に一大センセーションを巻き起こした(※個人の感想です)データ検証ライブラリーvalue-schemaの新バージョンがリリースされました。
今回の目玉はenumeration()
です。名前の通り、TypeScript(Deno含む)でenum
等の列挙型を使うときに真価を発揮します。
使いどころ
例えば、ユーザーの状態変更APIのようなものを作るとしましょう。取りうる状態は
"active"
, "inactive"
,
"banned"
の3種類の文字列とします。
従来のvalue-schemaでは、以下のようなコードで検証できます。
import vs from "value-schema"; interface User { state: string; }; const user: User = vs.applySchemaObject({ state: vs.string({ only: ["active", "inactive", "banned"], }), }, input); // inputは外部からの入力値
もちろんこれでも問題なく動作するんですが、せっかく数種類しかないとわかっているのならTypeScriptの列挙型(enum
)を使いたいですよね。こんな感じで書ければ理想です。
import vs from "value-schema"; // 列挙型を定義 enum State { ACTIVE = "active", INACTIVE = "inactive", BANNED = "banned", }; interface User { state: State; // 文字列型ではなくState型として宣言 }; // エラー(文字列型をStateに変換できない) const user: User = vs.applySchemaObject({ state: vs.string({ only: [State.ACTIVE, State.INACTIVE, State.BANNED], }), }, input); // inputは外部からの入力値
でも、applySchemaObject()
にとってはstate
はあくまで文字列型なので、「文字列型をState
に変換できない」というエラーが発生してしまいます。
そんなときにenumeration()
を使うことで、このようにきれいに記述できます。
import vs from "value-schema"; enum State { ACTIVE = "active", INACTIVE = "inactive", BANNED = "banned", }; interface User { state: State; } // 今度はOK(State型として認識される) const user: User = vs.applySchemaObject({ state: vs.enumeration({ only: [State.ACTIVE, State.INACTIVE, State.BANNED], }), }, input); // inputは外部からの入力値
何も考えずにstring()
をenumeration()
に変更するだけ。簡単ですね。const enum
を使う場合でも書き方は全く同じです。
もちろん数値の列挙型や、文字列と数値が混在した列挙型でも問題なく動作します。
「リテラル型のunion」を使う場合
ところで、TypeScript界隈ではenum
ではなく「リテラル型のunion」を使おうという風潮があります。詳しくは「さようなら、TypeScript enum」をご覧ください。
enumerate()
は、このリテラル型のunionにも対応できます。書き方はenum
と同じです。
import vs from "value-schema"; // リテラル型のunion const State = { ACTIVE: "active", INACTIVE: "inactive", BANNED: "banned", } as const; type State = typeof State[keyof typeof State]; // "active" | "inactive" | "banned" interface User { state: State; } // これもOK(State型として認識される) const user: User = vs.applySchemaObject({ state: vs.enumeration({ only: [State.ACTIVE, State.INACTIVE, State.BANNED] as const, }), }, input); // inputは外部からの入力値
type State = ...
のあたりがちょっと複雑ですが、これはリテラル型のunionを使うときのイディオムみたいなものなので無心で写経してください。
即値を使う場合
あまりないと思いますが、シンボルではなく即値を使いたい場合はちょっとだけ工夫が必要です。
import vs from "value-schema"; // リテラル型のunion type State = "active" | "inactive" | "banned"; interface User { state: State; } // これもOK(State型として認識される) const user: User = vs.applySchemaObject({ state: vs.enumeration({ only: ["active", "inactive", "banned"] as const, // constアサーションが必要 }), }, input); // inputは外部からの入力値
as const
がないと、only
に渡されるのは文字列配列なので、文字列型をState
に変換できないというエラーが出ます。
注意事項
最後に注意と言うほど大げさなものではないですが注意事項を1つだけ。
only
プロパティーは必須です。
理由は言うまでもないと思いますが、only
がないと何の値も受け付けないのでバリデーションの意味がないからです。
それでは快適なバリデーションライフを。
0 件のコメント:
コメントを投稿