☕ cafy
Simple, lightweight, flexible validator generator
cafyは、アサーションのようにメソッドチェーンで値のバリデーションを行うライブラリです。 cafyを使えばバリデーションを簡単かつ柔軟に書くことができます。すべてTypeScriptで書かれていて、型定義との相性も抜群です。 Try it out!
🤔 Why cafy
たとえばサーバー側で、クライアントから送信されてきたパラメータが正しい形式であるかどうか確認しないと、データベースのエラーやプログラムの例外を引き起こしたりする可能性があります。
「このパラメータはnullやundefinedではない文字列でなくてはならず、1文字以上100文字以下でなくてはならず、a-z0-9の文字種で構成されてなければならない」といった長いバリデーションを、cafyを使えば一行で簡潔に書くことができます。
例外も行うバリデーションごとに用意されているので、ユーザーにわかりやすいエラーメッセージを返すこともできます。
また、バリデータの型文字列を取得する機能があるので、それを使えばドキュメントを生成するときにも役立ちます。
TypeScriptのstrictNullChecks
オプションもサポートしています。
✨ 特徴
- 軽量 ... 依存関係無し。ブラウザでも使えます
- 簡単 ... 複雑にネストされたオブジェクトも直感的にバリデーションできる
- 柔軟 ... メソッドチェーンで制約を追加したり、独自の型を追加できる
- 強力な型サポート ... 型注釈不要で、バリデータに即した型を取得できる
strictNullChecks
サポートType Guard
サポートAssertion Functions
サポート
📦 Installation
Just:
npm install cafy
Happy validation👍
☘ Usage
TL;DR
;const isFruits = $strok;// true// true// false// false// false
まずその値がどんな型でなければならないかを示し、 そのあとに追加の制約をメソッドチェーンで追加していきます。
(以下のドキュメントでは、import $ from 'cafy';
している前提で書いていきます(実際にはcafy関数にどんな名前を付けるかは自由です)。)
たとえば 「それは文字列でなければならない」 という制約を表すにはこう書きます:
$str
range
メソッドを利用して、さらに 「10文字以上20文字以下でなければならない」 という制約を追加してみます:
$str
実際にバリデーションしてみましょう。
ok
メソッドに検証する値を渡すと、それが条件を満たせばtrue
が返り、そうでなければfalse
が返ります:
$str // true$str // false (短すぎるので)$str // false (長すぎるので)
もちろん、上記の例はこのようにまとめられます:
const validate = $strok;// true// false (短すぎるので)// false (長すぎるので)
cafyは様々な型をサポートしています:
- 文字列 ...
$.str
- 数値 ...
$.num
- 真理値 ...
$.bool
- 配列 ...
$.arr()
- オブジェクト ...
$.obj
- ユーザー定義型 ...
$.type()
- ユニオン ...
$.either()
- リテラル ...
$.literal()
- なんでも ...
$.any
ℹ JavaScriptの仕様上では配列はobjectですが、cafyでは配列はobjectとは見なされません。
後述するように、ユーザー定義型を使えば独自の型を追加することもできます。
それぞれの型がどのようなメソッドを持っているかなどは、APIのセクションをご確認ください。
null と undefined の扱い
cafyは、デフォルトでnull
もundefined
も許容しません。
null
やundefined
を許容したい場合は、これらのオプションを使用します:
undefined を許容する (optional)
デフォルトでundefined
はエラーになります:
$str // false
undefined
を許容する場合はoptional
を型の前にプリフィクスします:
$optionalstr // true
null を許容する (nullable)
デフォルトでnull
はエラーになります:
$str // false
null
を許容する場合はnullable
を型の前にプリフィクスします:
$nullablestr // true
null と undefined を許容する
nullable
とoptional
は併用できます:
$nullableoptionalstr...$optionalnullablestr...$optionalNullablestr...
undefined | null | |
---|---|---|
(default) | x | x |
optional |
o | x |
nullable |
x | o |
optional + nullable |
o | o |
📖 API
Context
cafyの実体はContext
クラスです。そして、cafyで実装されている全ての型はContext
クラスを継承したクラスです。
従って、Context
クラスにある次のメソッドおよびプロパティは全ての型で利用可能です。
メソッド
.get(value)
=> [any, Error]
テスト対象の値とテスト結果のペア(配列)を取得します。
.nok(value)
=> boolean
バリデーションを実行します。
合格した場合はfalse
で、そうでない場合はtrue
です。
.ok()
の否定です。
(nok は not ok の略です)
.ok(value)
=> boolean
バリデーションを実行します。
合格した場合はtrue
で、そうでない場合はfalse
です。
.test() == null
と同義です。
ℹ TypeScriptを使っているなら、このメソッドの結果で分岐を行うことで、以後対象の変数の型を推論することができます(Type Guard)。これについては、後述の「TypeScriptとの親和性」で詳しく説明します。
.pipe(fn)
=> Context
カスタムのバリデーションを実行できます。
引数の関数がtrue
を返すと妥当ということになり、false
またはError
を返すと不正な値とします。
$str // true$ // false
値が
null
またはundefined
のときはpipe
は実行されないため、pipe
内でnullチェックする必要はありません。
.assert(value)
=> void
バリデーションを実行します。
不合格の場合はError
をthrowします。
ℹ
TypeScriptを使っているなら、このメソッドを呼び出すことで、以後対象の変数の型を推論することができます。これについては、後述の「TypeScriptとの親和性」で詳しく説明します。現在正しく動作しません。詳しくは: https://github.com/microsoft/TypeScript/issues/34596
.test(value)
=> Error
バリデーションを実行します。
合格した場合はnull
で、そうでない場合はError
です。
.throw(value)
=> any
バリデーションを実行します。
合格した場合は値を返し、そうでない場合はError
をthrowします。
.getType()
=> string
このインスタンスの型を表す文字列を取得します。
例
型 | |
---|---|
$.str |
string |
$.optional.str |
string? |
$.nullable.str |
(string \| null) |
$.optional.nullable.str |
(string \| null)? |
$.arr($.str) |
string[] |
$.either($.str, $.num) |
(string \| number) |
プロパティ
.isOptional
: Boolean
optional
か否か(読み取り専用)
.isNullable
: Boolean
nullable
か否か(読み取り専用)
バリデータ: Any
any
Anyバリデータを使うと、「undefinedやnullはダメだけど、型は何でもいい」といった値を検証したいときに便利です:
$any // true
メソッド
Any固有のメソッドはありません。
バリデータ: Array
配列をバリデーションしたいときはこのバリデータを使用します。
配列の要素をバリデーションする
配列の各々の要素に対してバリデーションを定義できます:
$ // 数値の配列でなければならない$ // 10文字以上の文字列の配列でなければならない
もちろんarrayを入れ子にもできます:
$ // 「数値の配列」の配列でなければならない$ // 「10文字以上の文字列の配列」の配列でなければならない
メソッド
.min(threshold)
要素の数がthreshold
以上でなければならないという制約を追加します。
.max(threshold)
要素の数がthreshold
以下でなければならないという制約を追加します。
.range(min, max)
min
以上max
以下の数の要素を持っていなければならないという制約を追加します。
$ // true$ // false$ // false
ℹ️ range(30, 50)
はmin(30).max(50)
と同義です。
.length(length)
要素の数がlength
でなければならないという制約を追加します。
.unique()
ユニークな配列(=重複した値を持っていない)でなければならないという制約を追加します。
$ // true$ // false
.item(index, fn)
特定のインデックスの要素に対してカスタムのバリデーションを実行できます。
引数の関数がtrue
を返すと妥当ということになり、false
またはError
を返すと不正な値とします。
引数にはcafyインスタンスも渡せます。
$ // true$ // false
.each(fn)
各要素に対してカスタムのバリデーションを実行できます。
引数の関数がtrue
を返すと妥当ということになり、false
またはError
を返すと不正な値とします。
引数にはcafyインスタンスも渡せます。
$ // true$ // false
バリデータ: Boolean
boolboolean
真理値(true
かfalse
)をバリデーションしたいときはこのバリデータを使用します。
メソッド
固有のメソッドはありません。
バリデータ: Number
numnumber
数値をバリデーションしたいときはこのバリデータを使用します。
メソッド
.int()
整数でなければならないという制約を追加します。
$num // true$num // true$num // true$num // false$num // false$num // false$num // false
.min(threshold)
threshold
以上の数値でなければならないという制約を追加します。
.max(threshold)
threshold
以下の数値でなければならないという制約を追加します。
.range(min, max)
min
以上max
以下の数値でなければならないという制約を追加します。
ℹ️ range(30, 50)
はmin(30).max(50)
と同義です。
バリデータ: Object
objectprops
オブジェクトをバリデーションしたいときはこのバリデータを使用します。
プロパティを定義する
引数にプロパティの定義を与えて、複雑なオブジェクトも簡単にバリデーションできます。
例えば次のようなオブジェクトをバリデーションしたいとします:
const x =some:strawberry: 'pasta'alice: falsetachibana:bwh: 68 52 67thing: 42;
バリデータはこのように定義できます:
$// true
エラー
この型では、エラーに次のプロパティが含まれています:
prop
... バリデーションに不合格になったプロパティ名path
... 不合格になった子のプロパティまでのパスerror
... エラー内容
例えば次のような検証を行った時、エラーは次のようになります:
$;
Thrown:
{ Error: x.y.z: must-be-a-number
at ...
path: [ 'x', 'y', 'z' ],
error:
Error: must-be-a-number
at ... }
メソッド
.strict()
引数のプロパティ定義で言及した以外のプロパティを持っている場合にエラーにします。
デフォルト:
$ // true
strict:
$ // false
バリデータ: String
strstring
文字列をバリデーションしたいときはこのバリデータを使用します。
メソッド
.match(pattern)
与えられた正規表現とマッチしていなければならないという制約を追加します。
$str // true
.notMatch(pattern)
match
の否定。
.or(pattern)
与えられたパターン内の文字列のいずれかでなければならないという制約を追加します。
pattern
は文字列の配列または|
で区切られた文字列です。
$str // true$str // false$str // true
.notInclude(str | str[])
引数に与えられた文字列を含んでいてはならないという制約を追加します。
$str // false$str // false
.min(threshold)
threshold
以上の文字数でなければならないという制約を追加します。
.max(threshold)
threshold
以下の文字数でなければならないという制約を追加します。
.range(min, max)
min
以上max
以下の文字数でなければならないという制約を追加します。
ℹ️ range(30, 50)
はmin(30).max(50)
と同義です。
.length(length)
文字数がlength
でなければならないという制約を追加します。
Either
「文字列または数値」とか「真理値または真理値の配列」のようなバリデーションを行いたいときは、either
バリデータを使うことができます。
例:
// 文字列または数値$ // true
3種類以上の型
either
を任意の数入れ子にする事で実現できます:
// 文字列または数値または真理値$ // true
Literal
特定の値であることを保証するバリデーションを行いたいときは、literal
バリデータを使うことができます。
例:
// 文字列'foo'でなければならない$ // true
TypeScriptで使うときに便利です。
Use
既存のContextを拡張したいときに使います。
const other = $str;$optional // true$nullable // true
Type (ユーザー定義型)
typetype
cafyで標準で用意されているstring
やnumber
等の基本的な型以外にも、ユーザーが型を登録してバリデーションすることができます。
型を定義するには、まずcafyのContext
クラスを継承したContextクラスを作ります。
TypeScriptでの例:
;// あなたのクラス// あなたのクラスを検証するための、cafyのContextクラスを継承したクラス
バリデーションするときは、type
メソッドにクラスを渡します:
$.typeFooContext.oknew Foo; // true$.typeFooContext.ok'abc'; // false
カスタムメソッド
また、Context
を継承するクラスにメソッドを実装することで、Context中でそのメソッドを利用することもできます。
例として、上述のFooContext
に、「プロパティbar
が指定された値以上でなければならない」という制約を追加するメソッドmin
を定義してみましょう:
return this;
しているのは、メソッドチェーンできるようにするためです。
このメソッドを使う例:
;foo.bar = 42;$.typeFooContext.min40.okfoo; // true$.typeFooContext.min48.okfoo; // false
TypeScriptとの親和性
cafyはTypeScriptで書かれているため、強力な型定義を持ち、バリデーションに応じて変数の型を推論し、それ以降のフローで型を絞り込むことができます。
Type Guard
例えば、「x
は文字列でなければならない」とバリデーションした後のx
の型は明らかに文字列です(バリデータの実装にミスが無いと仮定した場合)。
ok
メソッドは型定義においてTypeScriptのType Guardを実装しており、ok
メソッドの返り値を使って条件分岐を行うと、そのスコープではバリデーションした変数の型が正しいものに絞り込まれます。これは、ok
メソッドにバリデーションに合格しない値を渡すとfalse
が返り分岐が実行されないことが判るので、分岐先スコープの変数の型は必ず求めている型になることが保証されるからです。
例:
;// この時点でxの型は unknownif $.str.okx
次のように書いても同じです:
詳しくはTypeScriptのType Guardのドキュメントを参照してください。
Assertion Functions
また、TypeScript 3.7で導入されたAssertion Functionsもサポートしていて、
assert
メソッドにある変数を渡して呼び出すと、その後の変数の型はバリデーションされた型になります。これは、assert
メソッドにバリデーションに合格しない値を渡すと、即座に例外がthrowされるので、
その後の変数の型は必ず求めている型になることが保証されるからです。TypeScript 3.7のAssertion Functionsによりこれを推論することが可能になります。例:
;// この時点でxの型は unknown$.str.assertx;x;// ↑この時点でxの型は string// この例ではxはnumberなので、実際にはここに到達することはない
詳しくはTypeScriptのAssertion Functionsのドキュメントを参照してください。
Literal Types
cafyの$.literal()
バリデータは、TypeScriptのconst assertionを使ったときのように型が値そのものになります。例:
if $.literal'foo'.okxif $.either$.literal'foo', $.literal'bar'.okx
Array, Object, Unionな型
配列、オブジェクト、ユニオン型といった複雑な型も、正しく推論することができます。 いくつかバリデーション後の型がどうなるのかの例を示します:
;// ↑ b の型は number[];// ↑ c の型は string | number;/* ↑ d の型は:{foo: {bar: {baz: number;};qux: boolean[][];};}*/
strictNullChecks
cafyはTypeScriptのstrictNullChecks
をサポートしていて、型定義においてnull
、undefined
、またはそうでないかを区別できます。例:
; // a の型は string; // b の型は string | undefined; // c の型は string | null; // d の型は string | undefined | null
Release Notes
Please see ChangeLog!