JavaScript における Schema Validation ツール Yup を試してみた。
こんにちは k-jun です。今回は JavaScript における Schema Validation ツール Yup を使ってみます。
https://github.com/jquense/yup
パッとみてわかることは以下ぐらいでしょうか。
まあ、最もよく理解するには使ってみることだと思うので、使っていきます。
npm install yup
schema というように、あるオブジェクトの定義として schema を作成すると、このデータ型に Cast してくれたり Validation が行える様子。 ひとまず作って見ます。
import yup from "yup" async function main() { let schema = yup.object().shape({ title: yup.string().required(), body: yup.string().required(), }); // schema.fields.title = yup.string().optional() const invalid = { body: "test body" } schema.validate(invalid).catch(err => console.error(err)) } main()
$ node index.js ValidationError: title is a required field at createError (/Users/keijun.kumagai/source-code/playground-javascript/node_modules/yup/lib/util/createValidation.js:54:21) at /Users/keijun.kumagai/source-code/playground-javascript/node_modules/yup/lib/util/createValidation.js:72:107 { value: { body: 'test body' }, path: 'title', type: 'required', errors: [ 'title is a required field' ], inner: [], params: { value: undefined, originalValue: undefined, label: undefined, path: 'title' } }
おお、大丈夫そう。"Schema objects are immutable, so each call of a method returns a new schema object" と書いてあるので、schema を変更することも試してみます。
import yup from "yup" async function main() { const schema = yup.object().shape({ title: yup.string().required(), body: yup.string().required(), }); schema.fields.title = yup.string().optional() const invalid = { body: "test body" } schema.validate(invalid).catch(err => console.error(err)) } main()
$ node index.js
通ってしまった...。意味を取り違えたのだろうか...。これで良いのだろうか...。
気を取り直して、他の実験もしていきます。まずは Cast を見てみる。
async function main() { const schema = yup.object().shape({ title: yup.string().required(), body: yup.string().required(), due: yup.date(), done: yup.boolean(), priority: yup.number(), }); const test = { title: "test todo", body: "", due: "2021-08-27", done: "0", priority: "10", } const x = schema.cast(test) console.log(x) } main()
$ node index.js { priority: 10, done: false, due: 2021-08-26T15:00:00.000Z, body: '', title: 'test todo' }
おお、Boolean の変換、Date の変換、Integer の変換なども諸々やってくれるようです。ORM のオブジェクトスキーマとして使用できる気もしてきました。
他、様々な便利メソッドが用意されているようです。以下ざっと見てみた感じ特に便利そうなものを紹介していきます。
ref
オブジェクトの他の箇所を参照として定義することが出来ます。オプション値を設定するなど、何かと便利そうです。
import { object, ref, string } from "yup" async function main() { let schema = object({ baz: ref('foo.bar'), foo: object({ bar: string(), }), x: ref('$x'), }); console.log(schema.cast({ foo: { bar: 'boom' } }, { context: { x: 5 } })); } main()
$ node index.js { x: 5, foo: { bar: 'boom' }, baz: 'boom' }
concat
2つの schema の定義を結合します。上手く使えば、柔軟な型の代わりのように動作するかもしれません。
import { object, ref, string, mixed } from "yup" async function main() { let schema = mixed(); schema = schema.concat(object().shape({ title: string().required(), })) schema = schema.concat(object().shape({ body: string().required(), })) schema.validate({}).catch(err => console.error(err)) } main()
と、ここまで見ていておもいましたが、これ TypeScript で良くないですか...? 他の関数をみても 型の結合、requried、non-required の設定。 default 値、型の和集合などと 全部 TypeScript の型の柔軟性に飲み込まれる気がします。
最終的に TypeScript でしっかりと型管理をすれば、問題ないのでは。それ以外に価値を自分はこの Yup に見いだせませんでした。
それでは今日はこのへんで。