背景
Javascript開発の多くの場所で、値を検証する必要がある状況に遭遇したため、この問題を何らかの形で解決する必要があることが明らかになりました。 この目的のために、次のタスクが設定されました。
以下を可能にするライブラリを開発します。
- データ型の検証。
- 無効なフィールドまたは要素の代わりにデフォルト値を設定します 。
- オブジェクトまたは配列の無効な部分を削除します 。
- エラーメッセージを受け取る
その根拠は次のとおりです。
- 習得が簡単
- 受信したコードの可読性。
- コード変更の容易さ
これらの目標を達成するために、 カルテット検証ライブラリが開発されました。
基本的な検証ブロック
幅広いタスクに適用できるように設計されているほとんどのシステムの中心にあるのは、アクション、データ、アルゴリズムなどの最も単純な要素です。 同様に、それらの構成の方法-より複雑な問題を解決するために、より複雑な最も単純な要素から何かを組み立てるために。
バリデーター
カルテットライブラリは、 バリデータの概念に基づいています。 このライブラリのバリデータは次の形式の関数です
function validator( value: any, { key: string|int, parent: any }, { key: string|int, parent: any }, ... ): boolean
この定義には、さらに詳しく説明する必要のあるものがいくつかあります。
function(...): boolean
バリデーター-検証の結果を計算し、検証の結果はブール値-trueまたはfalse 、それぞれ有効または無効
value: any
any-バリデーターがvalueの検証結果を計算することを示します。これは任意のjavascript値です。 バリデーターは、 検証された値を有効または無効に割り当てます。
{ key: string|int, parent: any }, ...
...-値のネストのレベルに応じて、検証された値が異なるコンテキストにある可能性があることを示します。 例で示しましょう
コンテキストなしの値の例
const value = 4; // . // : const isValueValid = validator(4)
配列コンテキストの値の例
// 0 1 2 3 4 const arr = [1, 2, 3, value, 5] // (k): 3 // : [1, 2, 3, value, 5] // value - const isValueValid = validator(4, { key: 3, parent: [1,2,3,4,5] })
オブジェクトのコンテキストでの値の例
const obj = { a: 1, b: 2, c: value, d: 8 } // 'c' // : { a: 1, b: 2, c: 4, d: 8 } // value - // : const isValueValid = validator(4, { key: 'c', parent: { a: 1, b: 2, c: 4, d: 8 } })
オブジェクトの構造はより大きなネストを持つことができるため、 さまざまなコンテキストについて話すのが理にかなっています
const arrOfObj = [{ a: 1, b: 2, c: value, d: 8 }, // ... ] // c 'c' // : { a: 1, b: 2, c: 4, d: 8 } // arrOfObj, // 0. // value - const isValueValid = validator( 4, { key: 'c', parent: { a: 1, b: 2, c: 4, d: 8 } } { key: 0, parent: [{ a: 1, b: 2, c: 4, d: 8 }] } )
などなど。
バリデータのこの定義は、 map、filter、some、everyなどの配列メソッドに引数として渡される関数の定義を思い出させる必要があります。
- これらの関数の最初の引数は配列要素です。
- 2番目の引数は、要素のインデックスです。
- 3番目の引数は配列自体です。
この場合のバリデーターはより一般化された関数です-配列内の要素のインデックスと配列だけでなく、配列のインデックスも取得します-その親とその親などで。
家を建てるのは何ですか?
上記のレンガは、javascript crutch 「beach」の上に横たわる他の「石の解決策」の中でも際立っていません。 したがって、より一貫性のある興味深いものから構築しましょう。 このために、 構成があります。
オブジェクト検証の超高層ビルを構築する方法は?
同意します。検証の説明がオブジェクトの説明と一致するようにオブジェクトを検証すると便利です。 このために、 validatorsのオブジェクト構成を使用します 。 次のようになります。
// quartet const quartet = require('quartet') // (v - validator) const v = quartet() // , // const objectSchema = { a: a => typeof a ==='string', // 'string' b: b => typeof b === 'number', // 'number' // ... } const compositeObjValidator = v(objectSchema) const obj = { a: 'some text', b: 2 } const isObjValid = compositeObjValidator(obj) console.log(isObjValid) // => true
ご覧のように、特定のフィールドに定義されたさまざまなバリデーターブリックから、オブジェクトバリデーター(まだかなり混雑している「小さな建物」)を組み立てることができます。 このために、バリデーターのコンポーザーv
を使用します。 バリデーターの場所でオブジェクトリテラルv
に会うたびに、彼はそれをオブジェクト構成と見なし、フィールドでオブジェクトバリデーターに変えます。
時にはすべてのフィールドを説明できません 。 たとえば、オブジェクトがデータ辞書の場合:
const quartet = require('quartet') const v = quartet() const isStringValidator = name => typeof name === 'string' const keyValueValidator = (value, { key }) => value.length === 1 && key.length === 1 const dictionarySchema= { dictionaryName: isStringValidator, ...v.rest(keyValueValidator) } const compositeObjValidator = v(dictionarySchema) const obj = { dictionaryName: 'next letter', b: 'c', c: 'd' } const isObjValid = compositeObjValidator(obj) console.log(isObjValid) // => true const obj2 = { dictionaryName: 'next letter', b: 'a', a: 'invalid value', notValidKey: 'a' } const isObj2Valid = compositeObjValidator(obj2) console.log(isObj2Valid) // => false
建築ソリューションを再利用する方法は?
上で見たように、単純なバリデーターを再利用する必要があります。 これらの例では、すでに「string type validator」を2回使用する必要がありました。
レコードを短くして読みやすくするために、カルテットライブラリはバリデータの文字列シノニムを使用します。 バリデーターの作曲者は、バリデーターがあるべき場所で文字列に遭遇するたびに、辞書でバリデーターを探して使用します。
デフォルトでは、最も一般的なバリデーターはすでにライブラリーに定義されています。
以下の例を検討してください。
v('number')(1) // => true v('number')('1') // => false v('string')('1') // => true v('string')(null) // => false v('null')(null) // => true v('object')(null) // => true v('object!')(null) // => false // ...
ドキュメントで説明された他の多く。
各アーチには独自の種類のレンガがありますか?
バリデータコンポーザ(関数v
)もバリデータファクトリです。 を返す多くの便利なメソッドが含まれているという意味で
- 関数バリデーター
- コンポーザーがバリデーターを作成するためのスキームとして認識する値
たとえば、配列の検証を見てみましょう。ほとんどの場合、配列の型をチェックし、すべての要素をチェックします。 これにはv.arrayOf(elementValidator)
メソッドを使用します。 たとえば、名前を持つポイントの配列を取得します。
const a = [ {x: 1, y: 1, name: 'A'}, {x: 2, y: 1, name: 'B'}, {x: -1, y: 2, name: 'C'}, {x: 1, y: 3, name: 'D'}, ]
ポイントの配列はオブジェクトの配列であるため、オブジェクトの構成を使用して配列の要素を検証することは理にかなっています。
const namedPointSchema = { x: 'number', // number - y: 'number', name: 'string' // string - }
次に、ファクトリーメソッドv.arrayOf
を使用して、配列全体のバリデーターを作成します。
const isArrayValid = v.arrayOf({ x: 'number', y: 'number', name: 'string' })
このバリデーターがどのように機能するかを見てみましょう:
isArrayValid(0) // => false isArrayValid(null) // => false isArrayValid([]) // => true isArrayValid([1, 2, 3]) // => false isArrayValid([ {x: 1, y: 1, name: 'A'}, {x: 2, y: 1, name: 'B'}, {x: -1, y: 2, name: 'C'}, {x: 1, y: 3, name: 'D'}, ]) // => true
これはファクトリーメソッドの1つにすぎません。各メソッドはドキュメントに記載されています。
上で見たように、 v.rest
、オブジェクト構成で指定されていないすべてのフィールドをチェックするオブジェクト構成を返すファクトリメソッドでもあります。 これは、 spread-operator
を使用して別のオブジェクト構成に埋め込むことができることを意味します。
それらのいくつかの使用を例として引用しましょう:
// quartet const quartet = require('quartet') // (v - validator) const v = quartet() // , const max = { name: 'Maxim', sex: 'male', age: 34, status: 'grandpa', friends: [ { name: 'Dima', friendDuration: '1 year'}, { name: 'Semen', friendDuration: '3 months'} ], workExperience: 2 } // , "" , // "" , "" - const nameSchema = v.and( 'not-empty', 'string', // name => name[0].toUpperCase() === name[0] // - ) const maxSchema = { name: nameSchema, // sex: v.enum('male', 'female'), // - . // "" age: v.and('non-negative', 'safe-integer'), status: v.enum('grandpa', 'non-grandpa'), friends: v.arrayOf({ name: nameSchema, // friendDuration: v.regex(/^[1-9]\d? (years?|months?)$/) }), workExperience: v.and('non-negative', 'safe-integer') } console.log(v(maxSchema)(max)) // => true
あるべきかどうか
有効なデータは、たとえば次のようなさまざまな形式をとることがよくあります。
-
id
は数値でも、文字列でもかまいません。 -
point
オブジェクトには、次元に応じて、いくつかの座標が含まれる場合と含まれない場合があります。 - 他の多くの場合。
オプションの検証を整理するために、別のタイプの構成-バリアント構成が提供されます。 可能なオプションのバリデーターの配列で表されます。 バリデータの少なくとも1つが有効性を報告した場合、オブジェクトは有効であると見なされます。
識別子の検証の例を考えてみましょう。
const isValidId = v([ v.and('not-empty', 'string'), // v.and('positive', 'safe-integer') // ]) isValidId('') // => false isValidId('asdba32bas321ab321adb321abds546ba98s7') // => true isValidId(0) // => false isValidId(1) // => true isValidId(1123124) // => true
ポイント検証の例:
const isPointValid = v([ { // - x dimension: v.enum(1), x: 'number', // v.rest false // , - ...v.rest(() => false) }, // - { dimension: v.enum(2), x: 'number', y: 'number', ...v.rest(() => false) }, // - x, y z { dimension: v.enum(3), x: 'number', y: 'number', z: 'number', ...v.rest(() => false) }, ]) // , , , - - isPointValid(1) // => false isPointValid(null) // => false isPointValid({ dimension: 1, x: 2 }) // => true isPointValid({ dimension: 1, x: 2, y: 3 // }) // => false isPointValid({ dimension: 2, x: 2, y: 3 }) // => true isPointValid({ dimension: 3, x: 2, y: 3, z: 4 }) // => true // ...
したがって、 作曲家が配列を見るときはいつでも、その配列のバリデーター要素の構成とみなし、その中の1つが値を有効と見なすと、検証の計算が停止し、値が有効であると認識されます。
ご覧のとおり、コンポーザはバリデータ関数をバリデータとしてだけでなく、バリデータ関数につながる可能性のあるすべてのものも考慮します。
検証タイプ | 例 | 作曲家が知覚したとおり |
---|---|---|
検証機能 | x => typeof x === 'bigint'
| 必要な値を呼び出しただけです |
オブジェクト構成 | { a: 'number' }
| 指定されたフィールドバリデータに基づいてオブジェクトのバリデータ関数を作成します |
バリアント構成 | ['number', 'string']
| 少なくとも1つのオプションで値を検証するための検証関数を作成します |
ファクトリメソッド呼び出しの結果 | v.enum('male', 'female')
| ほとんどのファクトリメソッドは検証関数を返します(オブジェクト構成を返すv.rest
除く)ので、それらは通常の検証関数のように扱われます |
これらの検証オプションはすべて有効であり、検証ツールがあるスキーマ内のどこででも使用できます。
その結果、操作スキームは常に次のようになりv(schema)
は検証関数を返します。 次に、特定の値でこの検証関数が呼び出されます。
v(schema)(value[, ...parents])
建設現場で何か事故がありましたか?
-まだ誰もいない
-彼らはします!
そのため、データが無効であり、無効の原因を特定できる必要があります。
このため、カルテットライブラリは説明メカニズムを提供します。 それは、内部または外部にかかわらず、バリデーターが検証済みデータの有効性を検出した場合に、 注釈を送信する必要があるという事実にあります。
これらの目的のために、バリデーターコンポーザーv
2番目の引数v
ます。 無効なデータの場合にv.explanation
配列に説明ノートを送信する副作用を追加します。
例として、配列を検証し、無効なすべての要素の数とその値を見つけたいとしましょう:
// - // const getExplanation = (value, { key: index }) => ({ invalidValue: value, index }) // , . // v.explanation // const arrValidator = v.arrayOf( v( 'number', // getExplanation // "", "" ) ) // , "" // , // // , const explainableArrValidator = v(arrValidator, 'this array is not valid') const arr = [1, 2, 3, 4, '5', '6', 7, '8'] explainableArrValidator(arr) // => false v.explanation // [ // { invalidValue: '5', index: 4 }, // { invalidValue: '6', index: 5 }, // { invalidValue: '8', index: 7 }, // 'this array is not valid' // ]
ご覧のとおり、説明の選択はタスクによって異なります。 時にはそれさえ必要ではありません。
時には、無効なフィールドで何かをする必要があります。 そのような場合、無効なフィールドの名前を説明として使用することは理にかなっています:
const objSchema = { a: v('number', 'a'), b: v('number', 'b'), c: v('string', 'c') } const isObjValid = v(objSchema) let invalidObj = { a: 1, b: '1', c: 3 } isObjValid(invalidObj) // => false v.explanation // ['b', 'c'] // console.error(`${v.explanation.join(', ')} is not valid`) // => b, c is not valid // (. ) invalidObj = v.omitInvalidProps(objSchema)(invalidObj) console.log(invalidObj) // => { a: 1 }
この説明のメカニズムを使用して、検証の結果に関連付けられた任意の動作を実装できます。
何でも説明できます:
- 必要な情報を含むオブジェクト。
- エラーを修正する関数。 (
getExplanation => function(invalid): valid
); - 無効なフィールドの名前、または無効な要素のインデックス。
- エラーコード
- そして、それはあなたの想像力に十分です。
物事が構築されていない場合の対処
検証エラーを修正することはめったにありません。 これらの目的のために、ライブラリは、エラーの場所と修正方法を記憶する副作用を持つバリデータを使用します。
-
v.default(validator, value)
-無効な値を記憶するバリデーターを返しますv.fix
の呼び出しv.fix
-デフォルト値を設定します -
v.filter(validator)
-無効な値を記憶するバリデーターを返しますv.fix
を呼び出すと、親からこの値を削除します -
v.addFix(validator, fixFunc)
-無効な値を記憶するバリデーターを返し、v.fixを呼び出すときにパラメーター(value、{key、parent}、...)でfixFuncを呼び出します。fixFunc
パートナーの1つを変更する必要があります-値を変更するには
const toPositive = (negativeValue, { key, parent }) => { parent[key] = -negativeValue } const objSchema = { a: v.default('number', 1), b: v.filter('string', ''), c: v.default('array', []), d: v.default('number', invalidValue => Number(invalidValue)), // pos: v.and( v.default('number', 0), // - 0 v.addFix('non-negative', toPositive) // - ) } const invalidObj = { a: 1, b: 2, c: 3, d: '4', pos: -3 } v.resetExplanation() // v() v(objSchema)(invalidObj) // => false // v.hasFixes() => true const validObj = v.fix(invalidObj) console.log(validObj) // => { a: 1, b: '', c: [], d: 4 }
家事はまだ重宝します
このライブラリには、検証に関連するアクションのユーティリティメソッドもあります。
方法 | 結果 |
---|---|
v.throwError
| 無効な場合、指定されたメッセージとともにTypeErrorをスローします。 |
v.omitInvalidItems
| 無効な要素(フィールド)のない新しい配列(または辞書オブジェクト)を返します。 |
v.omitInvalidProps
| 指定したオブジェクトバリデーターに従って、無効なフィールドのない新しいオブジェクトを返します。 |
v.validOr
| 有効な場合は値を返し、そうでない場合は指定されたデフォルト値に置き換えます。 |
v.example
| 指定された値がスキーマに適合するかどうかを確認します。 適合しない場合、エラーがスローされます。 ドキュメンテーションおよび回路テストとして機能 |
結果
タスクは次の方法で解決されました。
挑戦する | 解決策 |
---|---|
データ型検証 | デフォルトの名前付きバリデータ。 |
デフォルト値 | v.default
|
無効なパーツを削除する | v.filter
、 v.omitInvalidItems
および v.omitInvalidProps
。 |
習得が簡単 | シンプルなバリデーター、それらを複雑なバリデーターに合成する簡単な方法。 |
コードの読みやすさ | ライブラリの目標の1つは、検証スキーム自体を類推することでした |
検証済みオブジェクト。 | |
変更の容易さ | コンポジションの要素を習得し、独自の検証機能を使用して-コードの変更は非常に簡単です。 |
エラーメッセージ | エラーメッセージとしての説明。 または、説明に基づいたエラーコードの計算。 |
あとがき
このソリューションは、カスタム検証関数を埋め込む機能を備えた検証関数を迅速かつ便利に作成するように設計されました。 したがって、もしあれば、この記事を読んだ人からの修正、批判、改善オプションは歓迎です。 ご清聴ありがとうございました。