Cara membangun dan membangun

Latar belakang


Setelah bertemu di banyak tempat dalam pengembangan Javascript dengan situasi di mana perlu untuk memvalidasi nilai, menjadi jelas bahwa perlu untuk entah bagaimana menyelesaikan masalah ini. Untuk tujuan ini, tugas berikut telah ditetapkan:
Kembangkan perpustakaan yang akan memungkinkan:


  • memvalidasi tipe data ;
  • atur nilai default sebagai ganti bidang atau elemen yang tidak valid;
  • hapus bagian yang tidak valid dari suatu objek atau array;
  • Terima pesan kesalahan

Dasarnya adalah:


  • Mudah dipelajari
  • Keterbacaan kode yang diterima.
  • Kemudahan modifikasi kode

Untuk mencapai tujuan ini, perpustakaan validasi kuartet telah dikembangkan.


Batu bata validasi dasar


Inti dari sebagian besar sistem yang dirancang agar dapat diterapkan dalam berbagai tugas adalah elemen paling sederhana: tindakan, data, dan algoritma. Serta metode komposisi mereka - untuk mengumpulkan sesuatu yang lebih rumit dari elemen sederhana untuk menyelesaikan masalah yang lebih kompleks.


Validator


Pustaka kuartet didasarkan pada konsep validator . Validator di perpustakaan ini adalah fungsi dari formulir berikut


function validator( value: any, { key: string|int, parent: any }, { key: string|int, parent: any }, ... ): boolean 

Ada beberapa hal dalam definisi ini yang harus dijelaskan secara lebih rinci:


function(...): boolean - mengatakan bahwa validator - menghitung hasil validasi, dan hasil validasi adalah nilai boolean - benar atau salah , masing-masing valid atau tidak valid


value: any - menunjukkan bahwa validator - menghitung hasil validasi nilai , yang bisa berupa nilai javascript apa pun. Validator baik menetapkan nilai yang divalidasi menjadi valid atau tidak valid.


{ key: string|int, parent: any }, ... - menunjukkan bahwa nilai yang divalidasi dapat berada dalam konteks yang berbeda tergantung pada level apa dari nesting nilainya. Mari kita tunjukkan dengan contoh


Nilai contoh tanpa konteks apa pun


 const value = 4; //         . //         : const isValueValid = validator(4) 

Nilai contoh dalam konteks array


 //  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] }) 

Nilai contoh dalam konteks objek


 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 } }) 

Karena struktur dalam suatu objek dapat memiliki sarang yang lebih besar, masuk akal untuk berbicara tentang berbagai konteks


 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 }] } ) 

Dan sebagainya.


Tentang kesamaan dengan metode array

Definisi validator ini harus mengingatkan Anda tentang definisi fungsi yang dilewatkan sebagai argumen ke metode array, seperti: peta, filter, beberapa, setiap , dll.


  • Argumen pertama untuk fungsi-fungsi ini adalah elemen array.
  • Argumen kedua adalah indeks elemen.
  • Argumen ketiga adalah array itu sendiri.

Validator dalam hal ini adalah fungsi yang lebih umum - ia mengambil tidak hanya indeks elemen dalam array dan array, tetapi juga indeks array - dalam induk dan induknya, dan seterusnya.


Apa yang harus kita bangun rumah?


Batu bata yang dijelaskan di atas tidak menonjol di antara "solusi batu" lainnya yang tergeletak di "pantai" kruk javascript. Karena itu, mari kita membangun dari mereka, sesuatu yang lebih koheren dan menarik. Untuk ini kami memiliki komposisi .


Bagaimana cara membangun validasi gedung pencakar langit?


Setuju, akan lebih mudah untuk memvalidasi objek sedemikian rupa sehingga deskripsi validasi cocok dengan deskripsi objek. Untuk ini kita akan menggunakan komposisi objek validator . Ini terlihat seperti ini:


 //    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 

Seperti yang Anda lihat, dari batu bata validator berbeda yang ditentukan untuk bidang tertentu, kita dapat mengumpulkan validator objek - beberapa "bangunan kecil", yang masih cukup ramai - tetapi lebih baik daripada tanpa itu. Untuk ini, kami menggunakan komposer validator v . Setiap kali, bertemu objek literal v di tempat validator, itu akan menganggapnya sebagai komposisi objek, mengubahnya menjadi validator objek di bidangnya.


Terkadang kami tidak dapat menjelaskan semua bidang . Misalnya, ketika suatu objek adalah kamus data:


 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 

Bagaimana cara menggunakan kembali solusi konstruksi?


Seperti yang kita lihat di atas, ada kebutuhan untuk menggunakan kembali validator sederhana. Dalam contoh ini, kami sudah harus menggunakan "validator tipe string" sudah dua kali.


Untuk mempersingkat catatan dan meningkatkan keterbacaannya, pustaka kuartet menggunakan sinonim string validator. Setiap kali komposer validator menemukan string di tempat validator seharusnya, itu mencari kamus untuk validatornya dan menggunakannya .


Secara default, validator paling umum sudah ditentukan di perpustakaan.


Perhatikan contoh-contoh berikut:


 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 // ... 

dan banyak lainnya yang dijelaskan dalam dokumentasi .


Setiap lengkungan memiliki jenis batu bata sendiri?


Komposer validator (fungsi v ) juga merupakan pabrik validator. Dalam artian itu mengandung banyak metode berguna yang kembali


  • validator fungsi
  • nilai yang akan dirasakan oleh komposer sebagai skema untuk membuat validator

Sebagai contoh, mari kita lihat validasi array: paling sering terdiri dari memeriksa tipe array dan memeriksa semua elemennya. Kami akan menggunakan metode v.arrayOf(elementValidator) untuk ini. Misalnya, ambil array poin dengan nama.


  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'}, ] 

Karena array poin adalah array objek, masuk akal untuk menggunakan komposisi objek untuk memvalidasi elemen array.


 const namedPointSchema = { x: 'number', // number -       y: 'number', name: 'string' // string -       } 

Sekarang, menggunakan metode pabrik v.arrayOf , buat validator untuk seluruh array.


 const isArrayValid = v.arrayOf({ x: 'number', y: 'number', name: 'string' }) 

Mari kita lihat bagaimana validator ini bekerja:


 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 

Ini hanyalah salah satu metode pabrik, yang masing-masing dijelaskan dalam dokumentasi.


Seperti yang Anda lihat di atas, v.rest juga merupakan metode pabrik yang mengembalikan komposisi objek yang memeriksa semua bidang yang tidak ditentukan dalam komposisi objek. Itu berarti dapat tertanam dalam komposisi objek lain menggunakan spread-operator .


Mari kita kutip sebagai contoh penggunaan beberapa di antaranya:


 //    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 

Menjadi, atau tidak menjadi?


Sering terjadi bahwa data yang valid mengambil berbagai bentuk, misalnya:


  • id bisa berupa angka, atau bisa berupa string.
  • Objek point mungkin atau mungkin tidak mengandung beberapa koordinat, tergantung pada dimensi.
  • Dan banyak kasus lainnya.

Untuk mengatur validasi varian, disediakan jenis komposisi yang berbeda - komposisi varian. Ini diwakili oleh array validator dari opsi yang memungkinkan. Suatu objek dianggap valid ketika setidaknya satu validator melaporkan validitasnya.


Pertimbangkan contoh dengan validasi pengidentifikasi:


  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 

Contoh validasi poin:


 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 // ... 

Jadi, setiap kali seorang komposer melihat sebuah array, ia akan menganggapnya sebagai komposisi elemen validator dari array ini sedemikian rupa sehingga ketika salah satu dari mereka menganggap nilai valid, perhitungan validasi akan berhenti dan nilai tersebut akan diakui sebagai valid.


Seperti yang kita lihat, komposer menganggap tidak hanya fungsi validator sebagai validator, tetapi juga segala sesuatu yang dapat mengarah pada fungsi validator.


Jenis ValidatorContohSeperti yang dirasakan oleh komposer
fungsi validasix => typeof x === 'bigint'hanya memanggil nilai-nilai yang diperlukan
komposisi objek{ a: 'number' }membuat fungsi validator untuk objek berdasarkan validator bidang yang ditentukan
Komposisi varian['number', 'string']Membuat fungsi validator untuk memvalidasi nilai dengan setidaknya satu opsi
Hasil Panggilan Metode Pabrikv.enum('male', 'female')Sebagian besar metode pabrik mengembalikan fungsi validasi (dengan pengecualian v.rest , yang mengembalikan komposisi objek), sehingga mereka diperlakukan seperti fungsi validasi biasa

Semua opsi validator ini valid dan dapat digunakan di mana saja dalam skema di mana validator seharusnya berada.


Akibatnya, skema kerja selalu seperti ini: v(schema) mengembalikan fungsi validasi. Selanjutnya, fungsi validasi ini dipanggil pada nilai-nilai spesifik:
v(schema)(value[, ...parents])


Pernahkah Anda mengalami kecelakaan di lokasi konstruksi?


- Belum, belum satu
- Mereka akan!


Kebetulan data tidak valid dan kami harus dapat menentukan penyebab tidak valid.


Untuk ini, perpustakaan kuartet menyediakan mekanisme penjelasan . Terdiri dari fakta bahwa dalam kasus ketika validator, baik internal maupun eksternal, mendeteksi validitas data yang diverifikasi, ia harus mengirim catatan penjelasan .


Untuk tujuan ini, argumen kedua komposer validator v . Itu menambahkan efek samping dari mengirim catatan penjelasan ke array v.explanation dalam kasus data tidak valid.


Contoh, mari kita memvalidasi array, dan ingin mencari tahu jumlah semua elemen yang tidak valid dan nilainya:


  //   -     //   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' // ] 

Seperti yang Anda lihat, pilihan penjelasan tergantung pada tugas. Kadang-kadang bahkan tidak perlu.


Terkadang kita perlu melakukan sesuatu dengan bidang yang tidak valid. Dalam kasus seperti itu, masuk akal untuk menggunakan nama bidang yang tidak valid sebagai penjelasan :


 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 } 

Dengan mekanisme penjelasan ini, Anda dapat menerapkan perilaku apa pun yang terkait dengan hasil validasi.


Penjelasan bisa berupa apa saja:


  • sebuah objek yang berisi informasi yang diperlukan;
  • fungsi yang memperbaiki kesalahan. ( getExplanation => function(invalid): valid );
  • nama bidang tidak valid, atau indeks elemen tidak valid;
  • kode kesalahan
  • dan semua itu sudah cukup untuk imajinasi Anda.

Apa yang harus dilakukan ketika segala sesuatu tidak dibangun?


Memperbaiki kesalahan validasi bukanlah tugas yang jarang. Untuk tujuan ini, perpustakaan menggunakan validator dengan efek samping yang mengingat tempat kesalahan dan cara memperbaikinya.


  • v.default(validator, value) - mengembalikan validator yang mengingat nilai yang tidak valid, dan pada saat memanggil v.fix - menetapkan nilai default
  • v.filter(validator) - mengembalikan validator yang mengingat nilai yang tidak valid, dan pada saat memanggil v.fix - menghapus nilai ini dari induknya
  • v.addFix(validator, fixFunc) - mengembalikan validator yang mengingat nilai yang tidak valid, dan pada saat memanggil v.fix - memanggil fixFunc dengan parameter (nilai, {key, parent}, ...). fixFunc - harus bermutasi salah satu mitra - untuk mengubah nilai

 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 } 

Pekerjaan masih berguna


Ada juga metode utilitas untuk tindakan validasi di perpustakaan ini:


MetodeHasil
v.throwErrorJika tidak valid, lempar TypeError dengan pesan yang diberikan.
v.omitInvalidItemsMengembalikan array baru (atau objek kamus) tanpa elemen (bidang) yang tidak valid.
v.omitInvalidPropsMengembalikan objek baru tanpa bidang yang tidak valid, sesuai dengan validator objek yang ditentukan.
v.validOrMengembalikan nilai jika valid, jika tidak maka akan menggantinya dengan nilai default yang ditentukan.
v.exampleCek apakah nilai yang diberikan sesuai dengan skema. Jika mereka tidak cocok, kesalahan dilemparkan. Berfungsi sebagai dokumentasi dan pengujian sirkuit

Hasil


Tugas diselesaikan dengan cara-cara berikut:


TantanganSolusi
Validasi Jenis DataValidator bernama default.
Nilai defaultv.default
Menghapus bagian yang tidak validv.filter , v.omitInvalidItems dan v.omitInvalidProps .
Mudah dipelajariValidator sederhana, cara sederhana untuk menyusunnya menjadi validator yang kompleks.
Keterbacaan kodeSalah satu tujuan perpustakaan adalah menyamakan skema validasi itu sendiri
benda yang divalidasi.
Kemudahan modifikasiSetelah menguasai elemen komposisi dan menggunakan fungsi validasi Anda sendiri - mengubah kodenya cukup sederhana.
Pesan kesalahanPenjelasan dalam bentuk pesan kesalahan. Atau perhitungan kode kesalahan berdasarkan penjelasan.

Kata penutup


Solusi ini dirancang untuk membuat fungsi validator dengan cepat dan mudah dengan kemampuan untuk menyematkan fungsi validasi khusus. Karena itu, jika ada, koreksi, kritik, dan opsi perbaikan dari mereka yang membaca artikel ini dipersilakan. Terima kasih atas perhatian anda

Source: https://habr.com/ru/post/id429916/


All Articles