├── .gitignore ├── .travis.yml ├── package.json ├── LICENSE ├── index.d.ts ├── tsconfig.json ├── tests ├── jest.config.js └── test.js ├── index.js └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "stable" 4 | sudo: false 5 | before_script: 6 | - npm install 7 | script: 8 | - npm test -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "checktypes-js", 3 | "version": "1.0.5", 4 | "description": "checktypes-js is a package to help you check the types of a item variable using simple type or scheme for an object ", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "jest" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/ultd/checktypes-js.git" 12 | }, 13 | "keywords": [ 14 | "checktypes", 15 | "check", 16 | "types", 17 | "scheme", 18 | "object", 19 | "type", 20 | "checking" 21 | ], 22 | "author": "ultd", 23 | "license": "MIT", 24 | "bugs": { 25 | "url": "https://github.com/ultd/checktypes-js/issues" 26 | }, 27 | "homepage": "https://github.com/ultd/checktypes-js#readme", 28 | "devDependencies": { 29 | "jest": "^23.6.0" 30 | }, 31 | "typings": "./index.d.ts" 32 | } 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | (The MIT License) 2 | 3 | Copyright (c) 2018 Zain Ahmad Abbasi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | 'Software'), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | declare namespace checkTypes { 2 | interface CheckTypesError { 3 | propertyName: string 4 | expectedType: string 5 | receivedType: string 6 | required: boolean 7 | } 8 | function getType( 9 | element: string | number | boolean | symbol | Array | object 10 | ): string 11 | function $required(keys: string[]): string[] 12 | function setVal( 13 | passedObjectOrArray: object | Array, 14 | accessorString: string, 15 | newValue: string | number | boolean | symbol | Array | object, 16 | depth: number 17 | ): string | number | boolean | symbol | Array | object 18 | function getVal( 19 | passedObjectOrArray: object | Array, 20 | accessorString: string, 21 | depth: number 22 | ): string | number | boolean | symbol | Array | object 23 | function typeToString(type: Function): string 24 | function stringToType( 25 | typeString: 26 | | "String" 27 | | "Array" 28 | | "Object" 29 | | "Number" 30 | | "Boolean" 31 | | "Symbol" 32 | | "Null" 33 | | "Undefined" 34 | ): Function 35 | function propsFromString(accessorString: string): string[] 36 | } 37 | 38 | declare function checkTypes( 39 | passedItem: string | number | boolean | symbol | any[] | object | null, 40 | passedType: Function | Array | object, 41 | callback?: Function 42 | ): [ 43 | Array | null, 44 | string | number | boolean | symbol | Array | object | any 45 | ] 46 | 47 | export = checkTypes 48 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Basic Options */ 4 | // "incremental": true, /* Enable incremental compilation */ 5 | "target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */ 6 | "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ 7 | // "lib": [], /* Specify library files to be included in the compilation. */ 8 | // "allowJs": true, /* Allow javascript files to be compiled. */ 9 | // "checkJs": true, /* Report errors in .js files. */ 10 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ 11 | // "declaration": true, /* Generates corresponding '.d.ts' file. */ 12 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ 13 | // "sourceMap": true, /* Generates corresponding '.map' file. */ 14 | // "outFile": "./", /* Concatenate and emit output to single file. */ 15 | // "outDir": "./", /* Redirect output structure to the directory. */ 16 | // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ 17 | // "composite": true, /* Enable project compilation */ 18 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ 19 | // "removeComments": true, /* Do not emit comments to output. */ 20 | // "noEmit": true, /* Do not emit outputs. */ 21 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */ 22 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ 23 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ 24 | 25 | /* Strict Type-Checking Options */ 26 | "strict": true, /* Enable all strict type-checking options. */ 27 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ 28 | // "strictNullChecks": true, /* Enable strict null checks. */ 29 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */ 30 | // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ 31 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ 32 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ 33 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ 34 | 35 | /* Additional Checks */ 36 | // "noUnusedLocals": true, /* Report errors on unused locals. */ 37 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 38 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 39 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 40 | 41 | /* Module Resolution Options */ 42 | // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ 43 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ 44 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ 45 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ 46 | // "typeRoots": [], /* List of folders to include type definitions from. */ 47 | // "types": [], /* Type declaration files to be included in compilation. */ 48 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ 49 | "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ 50 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ 51 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 52 | 53 | /* Source Map Options */ 54 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ 55 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 56 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ 57 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ 58 | 59 | /* Experimental Options */ 60 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ 61 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /tests/jest.config.js: -------------------------------------------------------------------------------- 1 | // For a detailed explanation regarding each configuration property, visit: 2 | // https://jestjs.io/docs/en/configuration.html 3 | 4 | module.exports = { 5 | // All imported modules in your tests should be mocked automatically 6 | // automock: false, 7 | 8 | // Stop running tests after the first failure 9 | // bail: false, 10 | 11 | // Respect "browser" field in package.json when resolving modules 12 | // browser: false, 13 | 14 | // The directory where Jest should store its cached dependency information 15 | // cacheDirectory: "/tmp/jest_rs", 16 | 17 | // Automatically clear mock calls and instances between every test 18 | clearMocks: true, 19 | 20 | // Indicates whether the coverage information should be collected while executing the test 21 | // collectCoverage: false, 22 | 23 | // An array of glob patterns indicating a set of files for which coverage information should be collected 24 | // collectCoverageFrom: null, 25 | 26 | // The directory where Jest should output its coverage files 27 | coverageDirectory: "coverage", 28 | 29 | // An array of regexp pattern strings used to skip coverage collection 30 | // coveragePathIgnorePatterns: [ 31 | // "/node_modules/" 32 | // ], 33 | 34 | // A list of reporter names that Jest uses when writing coverage reports 35 | // coverageReporters: [ 36 | // "json", 37 | // "text", 38 | // "lcov", 39 | // "clover" 40 | // ], 41 | 42 | // An object that configures minimum threshold enforcement for coverage results 43 | // coverageThreshold: null, 44 | 45 | // Make calling deprecated APIs throw helpful error messages 46 | // errorOnDeprecated: false, 47 | 48 | // Force coverage collection from ignored files usin a array of glob patterns 49 | // forceCoverageMatch: [], 50 | 51 | // A path to a module which exports an async function that is triggered once before all test suites 52 | // globalSetup: null, 53 | 54 | // A path to a module which exports an async function that is triggered once after all test suites 55 | // globalTeardown: null, 56 | 57 | // A set of global variables that need to be available in all test environments 58 | // globals: {}, 59 | 60 | // An array of directory names to be searched recursively up from the requiring module's location 61 | // moduleDirectories: [ 62 | // "node_modules" 63 | // ], 64 | 65 | // An array of file extensions your modules use 66 | // moduleFileExtensions: [ 67 | // "js", 68 | // "json", 69 | // "jsx", 70 | // "node" 71 | // ], 72 | 73 | // A map from regular expressions to module names that allow to stub out resources with a single module 74 | // moduleNameMapper: {}, 75 | 76 | // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader 77 | // modulePathIgnorePatterns: [], 78 | 79 | // Activates notifications for test results 80 | // notify: false, 81 | 82 | // An enum that specifies notification mode. Requires { notify: true } 83 | // notifyMode: "always", 84 | 85 | // A preset that is used as a base for Jest's configuration 86 | // preset: null, 87 | 88 | // Run tests from one or more projects 89 | // projects: null, 90 | 91 | // Use this configuration option to add custom reporters to Jest 92 | // reporters: undefined, 93 | 94 | // Automatically reset mock state between every test 95 | // resetMocks: false, 96 | 97 | // Reset the module registry before running each individual test 98 | // resetModules: false, 99 | 100 | // A path to a custom resolver 101 | // resolver: null, 102 | 103 | // Automatically restore mock state between every test 104 | // restoreMocks: false, 105 | 106 | // The root directory that Jest should scan for tests and modules within 107 | // rootDir: null, 108 | 109 | // A list of paths to directories that Jest should use to search for files in 110 | // roots: [ 111 | // "" 112 | // ], 113 | 114 | // Allows you to use a custom runner instead of Jest's default test runner 115 | // runner: "jest-runner", 116 | 117 | // The paths to modules that run some code to configure or set up the testing environment before each test 118 | // setupFiles: [], 119 | 120 | // The path to a module that runs some code to configure or set up the testing framework before each test 121 | // setupTestFrameworkScriptFile: null, 122 | 123 | // A list of paths to snapshot serializer modules Jest should use for snapshot testing 124 | // snapshotSerializers: [], 125 | 126 | // The test environment that will be used for testing 127 | testEnvironment: "node", 128 | 129 | // Options that will be passed to the testEnvironment 130 | // testEnvironmentOptions: {}, 131 | 132 | // Adds a location field to test results 133 | // testLocationInResults: false, 134 | 135 | // The glob patterns Jest uses to detect test files 136 | // testMatch: [ 137 | // "**/__tests__/**/*.js?(x)", 138 | // "**/?(*.)+(spec|test).js?(x)" 139 | // ], 140 | 141 | // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped 142 | // testPathIgnorePatterns: [ 143 | // "/node_modules/" 144 | // ], 145 | 146 | // The regexp pattern Jest uses to detect test files 147 | // testRegex: "", 148 | 149 | // This option allows the use of a custom results processor 150 | // testResultsProcessor: null, 151 | 152 | // This option allows use of a custom test runner 153 | // testRunner: "jasmine2", 154 | 155 | // This option sets the URL for the jsdom environment. It is reflected in properties such as location.href 156 | // testURL: "http://localhost", 157 | 158 | // Setting this value to "fake" allows the use of fake timers for functions such as "setTimeout" 159 | // timers: "real", 160 | 161 | // A map from regular expressions to paths to transformers 162 | // transform: null, 163 | 164 | // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation 165 | // transformIgnorePatterns: [ 166 | // "/node_modules/" 167 | // ], 168 | 169 | // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them 170 | // unmockedModulePathPatterns: undefined, 171 | 172 | // Indicates whether each individual test should be reported during the run 173 | // verbose: null, 174 | 175 | // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode 176 | // watchPathIgnorePatterns: [], 177 | 178 | // Whether to use watchman for file crawling 179 | // watchman: true, 180 | }; 181 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Check if the passed item is correct type, returning 3 | * in 1st item an Array of errors if exist or null if not 4 | * and 2nd item the passed item itself. If using callback style, returns 5 | * function with errors Array and passed item as arguments 1 & 2. 6 | * 7 | * Examples: 8 | * 9 | * // Check a primitive type 10 | * checkTypes(66, Number) 11 | * 12 | * // Check an Array type 13 | * checkTypes(['john', 'adam', 'ahmad', 'sheila'], Array) 14 | * 15 | * // Check an Array type with also checking element types 16 | * checkTypes([55, 105, 33, -44], [Number]) 17 | * 18 | * // Check an Object type 19 | * cehckTypes({name: 'John', age: '44'}, Object) 20 | * 21 | * // Check an Object type with a schema 22 | * checkTypes({name: 'John', age: 44}, {name: String, age: Number}) 23 | * 24 | * // Check an object type with a schema with required fields using $required 25 | * checkTypes({username: 'briana1000', password: 'apassWord!'}, 26 | * {username: String, password: String, $required: ['username', 'password']}) 27 | * 28 | * @param {String|Array|Object|Boolean|Number|Symbol} passed 29 | * @param {String|Array|Object|Boolean|Number|Symbol|{}|[]|} schema 30 | * @param {Function} cb 31 | * @return {[[errors], (passed type)]} 32 | * @public 33 | */ 34 | 35 | function checkTypes(passed, schema, cb) { 36 | let callback = false 37 | if (typeof cb === 'function') callback = true 38 | // If no schema provided, skip checkTypes & return passed 39 | if (!schema) return callback ? cb(null, passed) : [null, passed] 40 | 41 | // Initialize validation errorsArray array 42 | const errorsArray = [] 43 | let error 44 | 45 | // Helper function to check the type of the element passed | ex: {name: 'john'} to 'Object' 46 | function getType(elem) { 47 | const retType = Object.prototype.toString.call(elem).slice(8, -1) 48 | if (retType === 'Undefined') return 'None' 49 | else if (retType === 'Null') return 'None' 50 | else return retType 51 | } 52 | 53 | // Helper function to convert schema definition types to string properly | ex: Object to 'Object' 54 | function schemaTypeToString(elem) { 55 | try { 56 | if (getType(elem) === 'Object') return 'Object' 57 | if (getType(elem) === 'Array') return 'Array' 58 | const retType = elem.toString().substring(9, elem.toString().indexOf('(')) 59 | if (retType === 'Undefined') return 'None' 60 | else return retType 61 | } catch (e) { 62 | if (e instanceof TypeError) return 'None' 63 | } 64 | } 65 | let returnItem 66 | 67 | const passedType = getType(passed) 68 | const schemaType = schemaTypeToString(schema) 69 | 70 | if (passedType !== schemaType) { 71 | error = [ 72 | { 73 | propertyName: '', 74 | expectedType: schemaType, 75 | receivedType: passedType, 76 | required: true 77 | } 78 | ] 79 | 80 | returnItem = passed 81 | return callback ? cb(error, returnItem) : [error, returnItem] 82 | } 83 | 84 | if (passedType === 'Object') { 85 | returnItem = checkObjectIsValid(schema === Object ? {} : schema, passed) 86 | } else if (passedType === 'Array') { 87 | returnItem = checkArrayIsValid(schema === Array ? [] : schema, passed) 88 | } else { 89 | returnItem = passed 90 | } 91 | 92 | /* 93 | If errorsArray exist in returnItem (meaning a mismatch between schema and returnItem), 94 | return errorsArray and returnItem 95 | */ 96 | if (errorsArray.length > 0) 97 | return callback ? cb(errorsArray, returnItem) : [errorsArray, returnItem] 98 | /* 99 | If no errorsArray found in returnItem, return null and passThrough object. 100 | */ else 101 | return callback ? cb(null, returnItem) : [null, returnItem] 102 | 103 | /* 104 | Schema Example: {name: String, age: Number, $required: ['name']} 105 | 106 | -- Required Settings -- 107 | A. [(property), (property), ...] : requires property names listed. 108 | B. $required(){ } : requires all properties in that level schema 109 | 110 | -- Use Cases -- 111 | ex1: {name: String, age: Number, $required: []} : 112 | {name: 'John', age: 33, $required: ['name']} : {name: (required), age: (not required)} 113 | 114 | ex2: {name: String, age: Number, $required: function()} : 115 | {name: 'John', age: 33, $required} : {name: (required), age: (required)} 116 | 117 | -- Error Cases -- 118 | ex1: {name: String, age: Number, $required: ['name']} : 119 | {firstName: 'John', age: 33, $required: ['name']} : 120 | {name: (required - MISSING!), firstName: (UNEXPECTED!) age: (not required)} 121 | -- name is required but missing, firstName is not expected. 122 | */ 123 | function checkObjectIsValid(schemaObject, passedObject, parentName) { 124 | const returningObject = {} 125 | const objPropertyNames = Object.keys(passedObject) 126 | const schemaPropertyNames = Object.keys(schemaObject) 127 | if (!schemaPropertyNames.length) return passedObject 128 | 129 | if (typeof schemaObject['$required'] === 'function') { 130 | const schemaPropertyNamesWithoutRequired = schemaPropertyNames.filter( 131 | prop => prop !== '$required' 132 | ) 133 | schemaObject['$required'] = schemaObject['$required']( 134 | schemaPropertyNamesWithoutRequired 135 | ) 136 | } else if (!schemaObject['$required']) { 137 | schemaObject.$required = [] 138 | } 139 | 140 | const $required = schemaObject['$required'] 141 | 142 | // Return array of missing fields in passedObject according to $required field in schemaObject 143 | const requiredMissing = $required.filter(reqdField => { 144 | return !objPropertyNames.includes(reqdField) 145 | }) 146 | 147 | // For each missing required field, push them to errorsArray array. 148 | requiredMissing.forEach(reqdMissItem => { 149 | // If this is a nested object, specify nested propertyName to error 150 | const propertyName = parentName 151 | ? parentName + '.' + reqdMissItem 152 | : reqdMissItem 153 | 154 | // If schemaObject has expected type defined, use that otherwise it expected none. 155 | const expType = schemaObject[reqdMissItem] 156 | ? schemaObject[reqdMissItem] 157 | : 'None' 158 | 159 | errorsArray.push({ 160 | propertyName: propertyName, 161 | expectedType: schemaTypeToString(schemaObject[reqdMissItem]), 162 | receivedType: getType(passedObject[reqdMissItem]), 163 | required: true 164 | }) 165 | }) 166 | 167 | objPropertyNames.forEach(propName => { 168 | // set property name to parentName.propName if parentName was passed else set to just propName 169 | const propertyName = parentName ? parentName + '.' + propName : propName 170 | const passedObjectPropertyType = getType(passedObject[propName]) 171 | const passedSchemaPropertyType = schemaTypeToString(schemaObject[propName]) 172 | 173 | let required 174 | 175 | // set required var to true or false depending on if it's required or not 176 | required = schemaObject.$required.find(req => { 177 | return req === propName 178 | }) 179 | 180 | required = required ? true : false 181 | 182 | // case in which a key of passedObject was not defined but required 183 | if (passedObjectPropertyType === 'None' && required) { 184 | errorsArray.push({ 185 | propertyName: propertyName, 186 | expectedType: passedSchemaPropertyType, 187 | receivedType: passedObjectPropertyType, 188 | required: required 189 | }) 190 | } 191 | 192 | // Object type validation 193 | else if (passedObjectPropertyType === 'Object') { 194 | // if passedObject property doesn't match schemaObject property type, push an error. 195 | if (passedSchemaPropertyType !== 'Object') { 196 | errorsArray.push({ 197 | propertyName: propertyName, 198 | expectedType: passedSchemaPropertyType, 199 | receivedType: passedObjectPropertyType, 200 | required: required 201 | }) 202 | returningObject[propName] = passedObject[propName] 203 | } else { 204 | // recursively check the passedObject property's props to match the schema definition. 205 | returningObject[propName] = checkObjectIsValid( 206 | schemaObject[propName], 207 | passedObject[propName], 208 | propertyName 209 | ) 210 | } 211 | 212 | // Array type validation 213 | } else if (passedObjectPropertyType === 'Array') { 214 | // if passedObject property doesn't match schemaObject property type, push an error. 215 | if (passedSchemaPropertyType !== 'Array') { 216 | errorsArray.push({ 217 | propertyName: propertyName, 218 | expectedType: passedSchemaPropertyType, 219 | receivedType: passedObjectPropertyType, 220 | required: required 221 | }) 222 | returningObject[propName] = passedObject[propName] 223 | } else { 224 | returningObject[propName] = checkArrayIsValid( 225 | schemaObject[propName], 226 | passedObject[propName], 227 | propertyName 228 | ) 229 | } 230 | } else if ( 231 | passedObjectPropertyType !== passedSchemaPropertyType && 232 | passedObjectPropertyType !== 'None' 233 | ) { 234 | errorsArray.push({ 235 | propertyName: propertyName, 236 | expectedType: passedSchemaPropertyType, 237 | receivedType: passedObjectPropertyType, 238 | required: required 239 | }) 240 | returningObject[propName] = passedObject[propName] 241 | } else { 242 | returningObject[propName] = passedObject[propName] 243 | } 244 | }) 245 | 246 | return returningObject 247 | } 248 | 249 | /* 250 | Schema Example: [TYPE] 251 | 252 | -- Use Cases -- 253 | ex1: [String] : ['John', '44'] : [String, String] 254 | ex2: [] : ['John', 44] : [ANY, ANY] 255 | ex4: [{name: String, age: Number, $required: ['name', 'age']}] : [Object] 256 | 257 | -- Error Cases -- 258 | ex1: [String] : ['John', 33] [String, ERROR - expected String] 259 | 260 | */ 261 | function checkArrayIsValid(schemaArray, passedArray, parentName = '') { 262 | // Not valid schema or passedArray is not array 263 | if (schemaTypeToString(schemaArray) !== 'Array' || !Array.isArray(passedArray)) { 264 | throw new Error( 265 | 'checkArrayIsValid needs both schemaArray & passedArray to be arrays.' 266 | ) 267 | } 268 | 269 | // No schema passed just need to make sure is array. 270 | if (!schemaArray.length) { 271 | return passedArray 272 | } else if (typeof schemaArray === 'function') { 273 | return passedArray 274 | } 275 | 276 | const returnArray = [] 277 | const schemaType = schemaTypeToString(schemaArray[0]) 278 | passedArray.forEach((element, i) => { 279 | const propertyName = parentName ? parentName + '[' + i + ']' : '[' + i + ']' 280 | const elementType = getType(element) 281 | if (elementType === 'Array') { 282 | if (elementType !== schemaType) { 283 | errorsArray.push({ 284 | propertyName: propertyName, 285 | expectedType: schemaType, 286 | receivedType: elementType, 287 | required: false 288 | }) 289 | returnArray[i] = element 290 | } else { 291 | returnArray[i] = checkArrayIsValid( 292 | schemaArray[0], 293 | element, 294 | propertyName 295 | ) 296 | } 297 | } else if (elementType === 'Object') { 298 | if (elementType !== schemaType) { 299 | errorsArray.push({ 300 | propertyName: propertyName, 301 | expectedType: schemaType, 302 | receivedType: elementType, 303 | required: false 304 | }) 305 | returnArray[i] = element 306 | } else { 307 | returnArray[i] = checkObjectIsValid( 308 | schemaArray[0], 309 | element, 310 | propertyName 311 | ) 312 | } 313 | } else { 314 | if (elementType !== schemaType) { 315 | errorsArray.push({ 316 | propertyName: propertyName, 317 | expectedType: schemaType, 318 | receivedType: elementType, 319 | required: false 320 | }) 321 | returnArray[i] = element 322 | } else { 323 | returnArray[i] = element 324 | } 325 | } 326 | }) 327 | return returnArray 328 | } 329 | } 330 | 331 | module.exports = checkTypes 332 | 333 | checkTypes.$required = function(allKeys) { 334 | return [].concat(allKeys) 335 | } 336 | 337 | checkTypes.getType = function(elem) { 338 | return Object.prototype.toString.call(elem).slice(8, -1) 339 | } 340 | 341 | checkTypes.getVal = function(passedObjOrArr, accessorString, stepBack = 0) { 342 | function get() { 343 | var passedObjectOrArray = passedObjOrArr 344 | if (stepBack < 0) throw new Error('stepBack must be zero or greater.') 345 | accessorString = accessorString.replace(/\[(\w+)\]/g, '.$1') 346 | accessorString = accessorString.replace(/^\./, '') 347 | var accessorStringsSplit = accessorString.split('.') 348 | if (accessorStringsSplit.length - stepBack < 1) 349 | throw new Error('stepBack cannot be more than total properties.') 350 | for (var i = 0; i < accessorStringsSplit.length - stepBack; i++) { 351 | var val = accessorStringsSplit[i] 352 | if (val in passedObjectOrArray) { 353 | passedObjectOrArray = passedObjectOrArray[val] 354 | } else { 355 | throw new Error('Invalid accessorString passed.') 356 | } 357 | } 358 | return passedObjectOrArray 359 | } 360 | return get() 361 | } 362 | 363 | checkTypes.setVal = function(passedObject, accessorString, newValue, stepBack = 0) { 364 | if (stepBack < 0) throw new Error('stepBack must be zero or greater.') 365 | function set(path, value) { 366 | path = path.replace(/\[(\w+)\]/g, '.$1') 367 | path = path.replace(/^\./, '') 368 | var arrOrObj = passedObject 369 | var pathList = path.split('.') 370 | var len = pathList.length - stepBack 371 | if (pathList.length - stepBack < 1) 372 | throw new Error('stepBack cannot be more than total properties.') 373 | for (var i = 0; i < len - 1; i++) { 374 | var elem = pathList[i] 375 | if (!arrOrObj[elem]) { 376 | throw new Error('Invalid accessorString passed.') 377 | } else { 378 | arrOrObj = arrOrObj[elem] 379 | } 380 | } 381 | arrOrObj[pathList[len - 1]] = value 382 | return arrOrObj[pathList[len - 1]] 383 | } 384 | return set(accessorString, newValue) 385 | } 386 | 387 | checkTypes.propsFromString = function(accessorString) { 388 | if (!accessorString.length) return [] 389 | accessorString = accessorString.replace(/\[(\w+)\]/g, '.$1') 390 | accessorString = accessorString.replace(/^\./, '') 391 | const accessorStringList = accessorString.split('.') 392 | return accessorStringList 393 | } 394 | 395 | checkTypes.typeToString = function(elem) { 396 | if (elem === undefined) return 'Undefined' 397 | if (elem === null) return 'Null' 398 | return elem.toString().substring(9, elem.toString().indexOf('(')) 399 | } 400 | 401 | checkTypes.stringToType = function(typeString) { 402 | if (typeof typeString !== 'string') throw new Error('argument must be a string type.') 403 | const typeStringsSupport = [ 404 | 'String', 405 | 'Array', 406 | 'Object', 407 | 'Number', 408 | 'Boolean', 409 | 'Symbol', 410 | 'Null', 411 | 'Undefined' 412 | ] 413 | 414 | const supported = typeStringsSupport.filter( 415 | supportedType => supportedType === typeString 416 | ) 417 | if (!supported.length) throw new Error('Not a supported type.') 418 | if (typeString === 'String') return String 419 | else if (typeString === 'Array') return Array 420 | else if (typeString === 'Object') return Object 421 | else if (typeString === 'Number') return Number 422 | else if (typeString === 'Boolean') return Boolean 423 | else if (typeString === 'Symbol') return Symbol 424 | else if (typeString === 'Undefined') return undefined 425 | else if (typeString === 'Null') return null 426 | else throw new Error("Couldn't return correct type for unknown reason.") 427 | } 428 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # checktypes-js [![CI status](https://travis-ci.com/ultd/checktypes-js.svg?branch=master)](https://travis-ci.com/ultd/checktypes-js) 2 | 3 | ![Checktypes-JS Logo](https://user-images.githubusercontent.com/12675427/74995506-14ecbd00-541f-11ea-8a1a-b4448fdcab0d.png) 4 | 5 | checktypes-js is a package to help you check the types of a passed variable. It also allows you to check types of an object using an object scheme or an Array and types of elements within the Array. This package also has typescript support. 6 | 7 | ## Installation 8 | 9 | `$ npm install checktypes-js --save` 10 | 11 | ## Introduction 12 | 13 | ### Sections 14 | 15 | 1. [Installation](#installation) 16 | 2. [Usage](#usage) 17 | - [Objects](#object-with-scheme-type-checking) 18 | - [simple object no properties type check](#simple-object-with-no-propertiess-type-checked) 19 | - [with defined required properties](#required-properties-in-scheme) 20 | - [with all required properties](#require-all-properties-in-scheme) 21 | - [Arrays](#array-type-checking) 22 | - [simple array type checking](#array-type-checking) 23 | - [with element type checking](#array-with-element-type-checking) 24 | - [Strings](#string-number-boolean-or-symbol-type-checking) 25 | - [Numbers](#string-number-boolean-or-symbol-type-checking) 26 | - [Booleans](#string-number-boolean-or-symbol-type-checking) 27 | - [Symbols](#string-number-boolean-or-symbol-type-checking) 28 | 3. [Helper functions](#helper-functions) 29 | - [getVal](#getval) 30 | - [setVal](#setval) 31 | - [getType](#gettype) 32 | - [typeToString](#typetostring) 33 | - [stringToType](#stringtotype) 34 | - [propsFromString](#propsfromstring) 35 | 4. [Contributing](#contributing) 36 | 5. [License](#license) 37 | 6. [Support](#support) 38 | 39 | ### Typescript Support 40 | 41 | Use `import { CheckTypesError } from 'checktype-js` for typings of the error objects. Use the package with maximum type checking advatage.. transpile time and run time type checking. What else could you ask for? 42 | 43 | ### Simple Example 44 | 45 | ```js 46 | const checkTypes = require('checktypes-js') 47 | 48 | const aNumber = 33 49 | 50 | const [error, numberPassed] = checkTypes(aNumber, Number) 51 | 52 | if(error) 53 | console.log(error) 54 | else 55 | // do stuff eith numberPassed 56 | ``` 57 | 58 | ### Callback or return style usage 59 | 60 | ```js 61 | const checkTypes = require('checktypes-js') 62 | 63 | /* Callback function style */ 64 | checkTypes(item: String|Number|Boolean|Symbol|Array|Object, 65 | type: String|Number|Boolean|Symbol|Array|Object, 66 | function(error, passedItem){ 67 | if(error){ 68 | // handle error 69 | } else { 70 | // do stuff with passedItem 71 | } 72 | }) 73 | 74 | // OR 75 | 76 | /* Return value style */ 77 | const [error, passedItem] = checkTypes(item: String|Number|Boolean|Symbol|Array|Object, 78 | type: String|Number|Boolean|Symbol|Array|Object) 79 | if(error){ 80 | // handle error 81 | } else { 82 | // do stuff with passedItem 83 | } 84 | ``` 85 | 86 | # Usage 87 | 88 | ### Object with Scheme type checking 89 | 90 | You can define a scheme with types and can be nested with other Objects or Arrays. 91 | 92 | ```js 93 | const checkTypes = require("checktypes-js") 94 | 95 | const personObject = { 96 | name: "Shelby", 97 | age: 32, 98 | isMarried: true, 99 | children: [{ name: "Hannah", age: 3 }, { name: "Billy", age: 6 }], 100 | address: { 101 | street: { 102 | number: "123", // <-- ERROR! String instead of Number 103 | name: "Main st." 104 | }, 105 | city: "Boston", 106 | state: "MA", 107 | country: "USA" 108 | }, 109 | comments: ["is very nice.", "has a good head on her shoulders"] 110 | } 111 | 112 | const personScheme = { 113 | name: String, 114 | age: Number, 115 | isMarried: Boolean, 116 | children: [{ name: String, age: Number }], 117 | address: { 118 | street: { 119 | number: Number, 120 | name: String 121 | }, 122 | city: String, 123 | state: String, 124 | country: String 125 | }, 126 | comments: [String] 127 | } 128 | 129 | checkTypes(personObject, personScheme, function(errors, passedObject) { 130 | if (errors) { 131 | console.log(errors) 132 | /* 133 | [ { propertyName: 'address.street.number', 134 | expectedType: 'Number', 135 | recievedType: 'String', 136 | required: false } ] 137 | */ 138 | 139 | // handle errors... 140 | } else { 141 | // do stuff with passedObject... 142 | } 143 | }) 144 | ``` 145 | 146 | #### Simple Object with no properties's type checked 147 | 148 | ```js 149 | const checkTypes = require('checktypes-js') 150 | 151 | const objectToCheck = { 152 | username: 'hannah1010', 153 | password: 'securePass!', 154 | comments: ['Hey beautiful', 'You\'re a great human being!'] 155 | } 156 | 157 | checkTypes(objectToCheck, Object, (errors, passedObject) => { // or an pass {} instead of Object 158 | if(errors) { 159 | // handle any errors 160 | } else { 161 | // do stuff with passedObject 162 | }) 163 | ``` 164 | 165 | #### Required properties in scheme: 166 | 167 | You can also specify required fields using `$required : [{String name(s) of properties}]` 168 | 169 | ```js 170 | const checkTypes = require('checktypes-js') 171 | 172 | const scheme = { 173 | username: String, 174 | password: String, 175 | rememberMe: Boolean, 176 | $required: ['username', 'password', 'rememberMe'] 177 | } 178 | 179 | const objectToCheck = { 180 | username: 'hannah1010', 181 | password: 'securePass!', // ERROR! missing 'rememberMe' property 182 | } 183 | 184 | checkTypes(objectToCheck, scheme, (errors, passedObject) => { 185 | if(errors) { 186 | console.log(errors) 187 | /* 188 | [ { propertyName: 'rememberMe', 189 | expectedType: 'Boolean', 190 | recievedType: 'None', 191 | required: true } ] 192 | */ 193 | 194 | } else { 195 | // do stuff with passedObject 196 | }) 197 | ``` 198 | 199 | #### Require all properties in scheme: 200 | 201 | You also have the ability to specify all scheme properties as required. Import `$required` from package. 202 | 203 | ```js 204 | const checkTypes = require('checktypes-js') 205 | const { $required } = checkTypes 206 | 207 | const scheme = { 208 | username: String, 209 | password: String, 210 | rememberMe: Boolean, 211 | $required // just pass $required 212 | } 213 | 214 | const objectToCheck = { 215 | password: 'securePass!', 216 | rememberMe: false // ERROR! missing 'username' property 217 | } 218 | 219 | checkTypes(objectToCheck, scheme, (errors, passedObject) => { 220 | if(errors) { 221 | console.log(errors) 222 | /* 223 | [ { propertyName: 'username', 224 | expectedType: 'String', 225 | recievedType: 'None', 226 | required: true } ] 227 | */ 228 | } else { 229 | // do stuff with passedObject 230 | }) 231 | ``` 232 | 233 | ### Array type checking 234 | 235 | ```js 236 | const checkTypes = require("checktypes-js") 237 | 238 | const [errors, returnedArray] = checkTypes([44, true, "a sexy string"], Array) // can also pass [] 239 | 240 | if (errors) { 241 | // handle errors 242 | } else { 243 | // do stuff with returnedArray 244 | } 245 | ``` 246 | 247 | ### Array with element type checking 248 | 249 | ```js 250 | const checkTypes = require("checktypes-js") 251 | 252 | const [errors, passedArray] = checkTypes([44, "545", 11], [Number]) 253 | 254 | if (errors) { 255 | console.log(errors) 256 | /* 257 | [ { propertyName: '[1]', 258 | expectedType: 'Number', 259 | recievedType: 'String', 260 | required: false } ] 261 | */ 262 | } else { 263 | // do stuff with passedArray 264 | } 265 | ``` 266 | 267 | ### String, Number, Boolean or Symbol type checking 268 | 269 | ```js 270 | const checkTypes = require("checktypes-js") 271 | 272 | const someString = 55 273 | 274 | const [error, passedItem] = checkTypes(someString, String) 275 | 276 | if (error) { 277 | console.log(error) 278 | /* 279 | [ { propertyName: '' 280 | expectedType: 'String', 281 | recievedType: 'Number', 282 | required: true } ] 283 | */ 284 | } else { 285 | // do stuff with passedItem 286 | } 287 | ``` 288 | 289 | When type checking primitive types or when an Object or Array type mismatches at the parent/root level, the error object will have an empty string `''` for the propertyName as it's not a property where the error occured. Also, the parent/root level is always required. You wouldn't be type checking if you didn't need it ;) 290 | 291 | ## Helper Functions 292 | 293 | This library provides a few helper functions to help you come up with innovative ways to handle errors. User your imagination! 294 | 295 | ### getVal() 296 | 297 | Use this helper function to get a value within the passed Object or Array using propertyName String. 298 | 299 | ```js 300 | getVal({PassedItem Object|Array}, {PropertyName String}, {StepBack: Number}) 301 | ``` 302 | 303 | #### example: 304 | 305 | ```js 306 | const checkTypes = require("checktypes-js") 307 | const { getVal } = checkTypes 308 | 309 | const scheme = { 310 | request: { 311 | headers: [{ name: String, value: String }], 312 | type: String, 313 | body: String, 314 | statusCode: Number 315 | } 316 | } 317 | 318 | const requestObject = { 319 | requestDetails: { 320 | headers: [ 321 | { name: "Authentication", value: "Bearer ..." }, 322 | { name: "Content-Type", value: "application/json" }, 323 | { name: "Cache-Control", value: 0 } // ERROR! value should be String type 324 | ], 325 | type: "POST", 326 | body: "...", 327 | statusCode: 200 328 | } 329 | } 330 | 331 | checkTypes(requestObject, scheme, function(errors, passedObject) { 332 | if (errors) { 333 | console.log(errors) 334 | /* 335 | [ { propertyName: 'request.headers[2]value', 336 | expectedType: 'String', 337 | recievedType: 'Number', 338 | required: false } ] 339 | */ 340 | const value = getVal(passedObject, errors[0].propertyName) 341 | console.log(value) 342 | // 0 343 | 344 | /* Or if you want encompassing object use stepBack (3rd argument) */ 345 | 346 | const objectValue = getVal(passedObject, errors[0].propertyName, 1) 347 | console.log(objectValue) 348 | // { name: "Cache-Control", value: 0 } 349 | } else { 350 | // do stuff with passedObject 351 | } 352 | }) 353 | ``` 354 | 355 | You can use the `stepBack` argument to step X properties back. So if you want to access the whole `requestDetails` object in above example, `stepBack` using `stepBack` set to `3`. If you set stepBack farther than the number of properties that exist, function will throw an Error. 356 | 357 | ### setVal() 358 | 359 | Use this helper function to set a new value within the passed Object or Array using propertyName String. 360 | 361 | ```js 362 | setVal({PassedItem Object|Array}, {PropertyName String}, 363 | {NewValue String|Number|Boolean|Symbol|Object|Array}, 364 | {StepBack: Number}) 365 | ``` 366 | 367 | #### example: 368 | 369 | ```js 370 | const checkTypes = require("checktypes-js") 371 | const { getVal, setVal } = checkTypes 372 | 373 | const scheme = { 374 | request: { 375 | headers: [{ name: String, value: String }], 376 | type: String, 377 | body: String, 378 | statusCode: Number 379 | } 380 | } 381 | 382 | const requestObject = { 383 | requestDetails: { 384 | headers: [ 385 | { name: "Authentication", value: "Bearer ..." }, 386 | { name: "Content-Type", value: "application/json" }, 387 | { name: "Cache-Control", value: 0 } // ERROR! value should be String type 388 | ], 389 | type: "POST", 390 | body: "...", 391 | statusCode: 200 392 | } 393 | } 394 | 395 | checkTypes(requestObject, scheme, function(errors, passedObject) { 396 | if (errors) { 397 | console.log(errors) 398 | /* 399 | [ { propertyName: 'request.headers[2]value', 400 | expectedType: 'String', 401 | recievedType: 'Number', 402 | required: false } ] 403 | */ 404 | const { propertyName } = errors[0] 405 | const value = getVal(passedObject, propertyName) 406 | console.log(value) 407 | // 0 408 | if (!value) setVal(passedObject, propertyName, "no-cache") 409 | const changedValue = getVal(passedObject, propertyName) 410 | console.log(changedValue) 411 | // 'no-cache' 412 | } else { 413 | // do stuff with passedObject 414 | } 415 | }) 416 | ``` 417 | 418 | You can use `stepBack` argument 4th argument as a way to set value to the encompassing Object or Array. It is useful for cases as such: 419 | 420 | ```js 421 | const checkTypes = require("checktypes-js") 422 | const { getVal, setVal } = checkTypes 423 | 424 | const saleScheme = { 425 | order: { 426 | number: Number, 427 | items: [String], 428 | customer: { 429 | name: String, 430 | phone: String, 431 | address: { 432 | street: String, 433 | city: String, 434 | state: String, 435 | zipCode: String, 436 | country: String 437 | } 438 | } 439 | }, 440 | shipment: { 441 | postmarkId: Number, 442 | departed: Number, 443 | service: String 444 | } 445 | } 446 | 447 | const aSale = { 448 | order: { 449 | number: 3453434, 450 | items: ["purse", "ring", "jacket"], 451 | customer: { 452 | name: "Hannah Medcraft", 453 | phone: "214-000-3443", 454 | address: { 455 | street: "123 Main St", 456 | city: "Dallas", 457 | state: "TX", 458 | zipCode: 72034, 459 | country: "USA" 460 | } 461 | } 462 | }, 463 | shipment: { 464 | postmarkId: 3423452456675, 465 | departed: 15604955496, 466 | service: "UPS" 467 | } 468 | } 469 | 470 | checkTypes(aSale, saleScheme, function(errors, passedObject) { 471 | if (errors) { 472 | console.log(errors) 473 | /* 474 | [ { propertyName: 'order.customer.address.zipCode', 475 | expectedType: 'Number', 476 | recievedType: 'String', 477 | required: false } ] 478 | */ 479 | const { propertyName } = errors[0] 480 | const wholeAddress = getVal(passedObject, propertyName, 1) 481 | console.log(wholeAddress) 482 | /* 483 | { 484 | street: '123 Main St', 485 | city: 'Dallas', 486 | state: 'TX', 487 | zipCode: 72034, 488 | country: 'USA' 489 | } 490 | */ 491 | const newValidatedAddress = validateAddress(wholeAddress) 492 | setVal(passedObject, propertyName, newValidatedAddress, 1) 493 | 494 | const changedValue = getVal(passedObject, propertyName, 1) 495 | console.log(changedValue) 496 | /* 497 | { 498 | street: '123 Main Steet N', 499 | city: 'Irving', 500 | state: 'TX', 501 | zipCode: '72034-3453', 502 | country: 'USA' 503 | } 504 | */ 505 | } else { 506 | // do stuff with passedObject 507 | } 508 | }) 509 | ``` 510 | 511 | ### getType() 512 | 513 | Use this helper function to get the type of an item passed to it. 514 | 515 | ```js 516 | getType({PassedItem String|Number|Boolean|Symbol|Object|Array|Undefined|Null}) 517 | ``` 518 | 519 | #### example: 520 | 521 | ```js 522 | const checkTypes = require("checktypes-js") 523 | const { getType } = checkTypes 524 | 525 | const type = getType({ lambaRocks: true }) 526 | console.log(type) 527 | // 'Object' 528 | 529 | // OR 530 | 531 | const type = getType(67) 532 | console.log(type) 533 | // 'Number' 534 | ``` 535 | 536 | ### typeToString() 537 | 538 | Use this helper function to convert a type to a string. 539 | 540 | ```js 541 | typeToString({PassedType String|Number|Boolean|Symbol|Object|Array}) 542 | ``` 543 | 544 | #### example: 545 | 546 | ```js 547 | const checkTypes = require("checktypes-js") 548 | const { typeToString } = checkTypes 549 | 550 | const typeAsString = typeToString(Array) 551 | 552 | console.log(type) 553 | // 'Array' 554 | ``` 555 | 556 | ### stringToType() 557 | 558 | Use this helper function to convert a string type to a Javascript type. 559 | 560 | ```js 561 | stringToType({TypeString String}) 562 | ``` 563 | 564 | #### example: 565 | 566 | ```js 567 | const checkTypes = require("checktypes-js") 568 | const { stringToType } = checkTypes 569 | 570 | const returnedType = stringToType("Object") 571 | 572 | console.log(Object === returnedType) 573 | // true 574 | ``` 575 | 576 | ### propsFromString() 577 | 578 | Use this helper function to convert an accessor string to an array of property names (for use to traverse through an object if needed). 579 | 580 | ```js 581 | propsFromString({AccessorString String}) 582 | ``` 583 | 584 | #### example: 585 | 586 | ```js 587 | const checkTypes = require("checktypes-js") 588 | const { propsFromString } = checkTypes 589 | 590 | const anAccessorString = "order[1]address.zipCode" 591 | const propertiesArray = propsFromString(anAccessorString) 592 | 593 | console.log(propertiesArray) 594 | // ['order', '1', 'address', 'zipCode'] 595 | ``` 596 | 597 | ## Contributing 598 | 599 | Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change. 600 | 601 | ## License 602 | 603 | [MIT](https://choosealicense.com/licenses/mit/) 604 | 605 | ## Support 606 | 607 | Please open ticket, no email. 608 | -------------------------------------------------------------------------------- /tests/test.js: -------------------------------------------------------------------------------- 1 | const checkTypes = require("../index.js") 2 | const { 3 | $required, 4 | getVal, 5 | setVal, 6 | getType, 7 | stringToType, 8 | typeToString, 9 | propsFromString 10 | } = checkTypes 11 | 12 | /* Number Tests */ 13 | 14 | test("Type check for Number with a Number", () => { 15 | checkTypes(66, Number, err => { 16 | expect(err).toEqual(null) 17 | }) 18 | }) 19 | 20 | test("Type check for Number with a Boolean", () => { 21 | checkTypes(true, Number, err => { 22 | expect(err).toEqual( 23 | expect.arrayContaining([ 24 | expect.objectContaining({ 25 | expectedType: "Number", 26 | receivedType: "Boolean", 27 | required: true 28 | }) 29 | ]) 30 | ) 31 | }) 32 | }) 33 | 34 | test("Type check for Number with an Object", () => { 35 | checkTypes({ aNumber: 5 }, Number, err => { 36 | expect(err).toEqual( 37 | expect.arrayContaining([ 38 | expect.objectContaining({ 39 | expectedType: "Number", 40 | receivedType: "Object", 41 | required: true 42 | }) 43 | ]) 44 | ) 45 | }) 46 | }) 47 | 48 | test("Type check for Number with an Array", () => { 49 | checkTypes([4, 6], Number, err => { 50 | expect(err).toEqual( 51 | expect.arrayContaining([ 52 | expect.objectContaining({ 53 | expectedType: "Number", 54 | receivedType: "Array", 55 | required: true 56 | }) 57 | ]) 58 | ) 59 | }) 60 | }) 61 | 62 | test("Type check for Number with a String", () => { 63 | checkTypes("55", Number, err => { 64 | expect(err).toEqual( 65 | expect.arrayContaining([ 66 | expect.objectContaining({ 67 | expectedType: "Number", 68 | receivedType: "String", 69 | required: true 70 | }) 71 | ]) 72 | ) 73 | }) 74 | }) 75 | 76 | test("Type check for Number with a Symbol", () => { 77 | checkTypes(Symbol(5), Number, err => { 78 | expect(err).toEqual( 79 | expect.arrayContaining([ 80 | expect.objectContaining({ 81 | expectedType: "Number", 82 | receivedType: "Symbol", 83 | required: true 84 | }) 85 | ]) 86 | ) 87 | }) 88 | }) 89 | 90 | /* String Tests */ 91 | 92 | test("Type check for String with a String", () => { 93 | checkTypes("hello world", String, err => { 94 | expect(err).toEqual(null) 95 | }) 96 | }) 97 | 98 | test("Type check for String with a Number", () => { 99 | checkTypes(66, String, err => { 100 | expect(err).toEqual( 101 | expect.arrayContaining([ 102 | expect.objectContaining({ 103 | expectedType: "String", 104 | receivedType: "Number", 105 | required: true 106 | }) 107 | ]) 108 | ) 109 | }) 110 | }) 111 | 112 | test("Type check for String with a Boolean", () => { 113 | checkTypes(false, String, err => { 114 | expect(err).toEqual( 115 | expect.arrayContaining([ 116 | expect.objectContaining({ 117 | expectedType: "String", 118 | receivedType: "Boolean", 119 | required: true 120 | }) 121 | ]) 122 | ) 123 | }) 124 | }) 125 | 126 | test("Type check for String with an Object", () => { 127 | checkTypes({ name: "John" }, String, err => { 128 | expect(err).toEqual( 129 | expect.arrayContaining([ 130 | expect.objectContaining({ 131 | expectedType: "String", 132 | receivedType: "Object", 133 | required: true 134 | }) 135 | ]) 136 | ) 137 | }) 138 | }) 139 | 140 | test("Type check for String with an Array", () => { 141 | checkTypes(["a string array"], String, err => { 142 | expect(err).toEqual( 143 | expect.arrayContaining([ 144 | expect.objectContaining({ 145 | expectedType: "String", 146 | receivedType: "Array", 147 | required: true 148 | }) 149 | ]) 150 | ) 151 | }) 152 | }) 153 | 154 | test("Type check for String with a Symbol", () => { 155 | checkTypes(Symbol("a string"), String, err => { 156 | expect(err).toEqual( 157 | expect.arrayContaining([ 158 | expect.objectContaining({ 159 | expectedType: "String", 160 | receivedType: "Symbol", 161 | required: true 162 | }) 163 | ]) 164 | ) 165 | }) 166 | }) 167 | 168 | /* Booelan Tests */ 169 | 170 | test("Type check for Boolean with a Boolean", () => { 171 | checkTypes(true, Boolean, err => { 172 | expect(err).toEqual(null) 173 | }) 174 | }) 175 | 176 | test("Type check for Boolean with a Number", () => { 177 | checkTypes(67, Boolean, err => { 178 | expect(err).toEqual( 179 | expect.arrayContaining([ 180 | expect.objectContaining({ 181 | expectedType: "Boolean", 182 | receivedType: "Number", 183 | required: true 184 | }) 185 | ]) 186 | ) 187 | }) 188 | }) 189 | 190 | test("Type check for Boolean with a String", () => { 191 | checkTypes("true", Boolean, err => { 192 | expect(err).toEqual( 193 | expect.arrayContaining([ 194 | expect.objectContaining({ 195 | expectedType: "Boolean", 196 | receivedType: "String", 197 | required: true 198 | }) 199 | ]) 200 | ) 201 | }) 202 | }) 203 | 204 | test("Type check for Boolean with an Object", () => { 205 | checkTypes({ success: false }, Boolean, err => { 206 | expect(err).toEqual( 207 | expect.arrayContaining([ 208 | expect.objectContaining({ 209 | expectedType: "Boolean", 210 | receivedType: "Object", 211 | required: true 212 | }) 213 | ]) 214 | ) 215 | }) 216 | }) 217 | 218 | test("Type check for Boolean with an Array", () => { 219 | checkTypes([false], Boolean, err => { 220 | expect(err).toEqual( 221 | expect.arrayContaining([ 222 | expect.objectContaining({ 223 | expectedType: "Boolean", 224 | receivedType: "Array", 225 | required: true 226 | }) 227 | ]) 228 | ) 229 | }) 230 | }) 231 | 232 | test("Type check for Boolean with a Symbol", () => { 233 | checkTypes(Symbol(false), Boolean, err => { 234 | expect(err).toEqual( 235 | expect.arrayContaining([ 236 | expect.objectContaining({ 237 | expectedType: "Boolean", 238 | receivedType: "Symbol", 239 | required: true 240 | }) 241 | ]) 242 | ) 243 | }) 244 | }) 245 | 246 | /* Symbol Tests */ 247 | 248 | test("Type check for Symbol with a Symbol", () => { 249 | checkTypes(Symbol(5), Symbol, err => { 250 | expect(err).toEqual(null) 251 | }) 252 | }) 253 | 254 | test("Type check for Symbol with a Number", () => { 255 | checkTypes(5, Symbol, err => { 256 | expect(err).toEqual( 257 | expect.arrayContaining([ 258 | expect.objectContaining({ 259 | expectedType: "Symbol", 260 | receivedType: "Number", 261 | required: true 262 | }) 263 | ]) 264 | ) 265 | }) 266 | }) 267 | 268 | test("Type check for Symbol with a Boolean", () => { 269 | checkTypes(true, Symbol, err => { 270 | expect(err).toEqual( 271 | expect.arrayContaining([ 272 | expect.objectContaining({ 273 | expectedType: "Symbol", 274 | receivedType: "Boolean", 275 | required: true 276 | }) 277 | ]) 278 | ) 279 | }) 280 | }) 281 | 282 | test("Type check for Symbol with a String", () => { 283 | checkTypes("a not so good symbol", Symbol, err => { 284 | expect(err).toEqual( 285 | expect.arrayContaining([ 286 | expect.objectContaining({ 287 | expectedType: "Symbol", 288 | receivedType: "String", 289 | required: true 290 | }) 291 | ]) 292 | ) 293 | }) 294 | }) 295 | 296 | test("Type check for Symbol with an Array", () => { 297 | checkTypes([Symbol(54)], Symbol, err => { 298 | expect(err).toEqual( 299 | expect.arrayContaining([ 300 | expect.objectContaining({ 301 | expectedType: "Symbol", 302 | receivedType: "Array", 303 | required: true 304 | }) 305 | ]) 306 | ) 307 | }) 308 | }) 309 | 310 | test("Type check for Symbol with an Object", () => { 311 | checkTypes({ symbol: Symbol(54) }, Symbol, err => { 312 | expect(err).toEqual( 313 | expect.arrayContaining([ 314 | expect.objectContaining({ 315 | expectedType: "Symbol", 316 | receivedType: "Object", 317 | required: true 318 | }) 319 | ]) 320 | ) 321 | }) 322 | }) 323 | 324 | /* Array Type Tests */ 325 | 326 | test("Type check for Array with an Array", () => { 327 | checkTypes(["an", "array"], Array, err => { 328 | expect(err).toEqual(null) 329 | }) 330 | }) 331 | 332 | test("Type check for Array with a Number", () => { 333 | checkTypes(55, Array, err => { 334 | expect(err).toEqual( 335 | expect.arrayContaining([ 336 | expect.objectContaining({ 337 | expectedType: "Array", 338 | receivedType: "Number", 339 | required: true 340 | }) 341 | ]) 342 | ) 343 | }) 344 | }) 345 | 346 | test("Type check for Array with a String", () => { 347 | checkTypes("an array", Array, err => { 348 | expect(err).toEqual( 349 | expect.arrayContaining([ 350 | expect.objectContaining({ 351 | expectedType: "Array", 352 | receivedType: "String", 353 | required: true 354 | }) 355 | ]) 356 | ) 357 | }) 358 | }) 359 | 360 | test("Type check for Array with a Boolean", () => { 361 | checkTypes(false, Array, err => { 362 | expect(err).toEqual( 363 | expect.arrayContaining([ 364 | expect.objectContaining({ 365 | expectedType: "Array", 366 | receivedType: "Boolean", 367 | required: true 368 | }) 369 | ]) 370 | ) 371 | }) 372 | }) 373 | 374 | test("Type check for Array with a Symbol", () => { 375 | checkTypes(Symbol([43]), Array, err => { 376 | expect(err).toEqual( 377 | expect.arrayContaining([ 378 | expect.objectContaining({ 379 | expectedType: "Array", 380 | receivedType: "Symbol", 381 | required: true 382 | }) 383 | ]) 384 | ) 385 | }) 386 | }) 387 | 388 | test("Type check for Array with an Object", () => { 389 | checkTypes({ list: [54, 56] }, Array, err => { 390 | expect(err).toEqual( 391 | expect.arrayContaining([ 392 | expect.objectContaining({ 393 | expectedType: "Array", 394 | receivedType: "Object", 395 | required: true 396 | }) 397 | ]) 398 | ) 399 | }) 400 | }) 401 | 402 | /* Array Literal Tests */ 403 | 404 | test("Type check for Array literal with an Array", () => { 405 | checkTypes(["an", "literal", "array"], [], err => { 406 | expect(err).toEqual(null) 407 | }) 408 | }) 409 | 410 | test("Type check for Array literal with an Array of Numbers", () => { 411 | checkTypes([54, 65, 65], [Number], err => { 412 | expect(err).toEqual(null) 413 | }) 414 | }) 415 | 416 | test("Type check for Array literal with an Array of Strings", () => { 417 | checkTypes(["john", "adams"], [String], err => { 418 | expect(err).toEqual(null) 419 | }) 420 | }) 421 | 422 | test("Type check for Array literal with an Array of Booleans", () => { 423 | checkTypes([true, false], [Boolean], err => { 424 | expect(err).toEqual(null) 425 | }) 426 | }) 427 | 428 | test("Type check for Array literal with an Array of Object instance", () => { 429 | checkTypes([{ anObject: true }, { name: "john" }], [Object], err => { 430 | expect(err).toEqual(null) 431 | }) 432 | }) 433 | 434 | test("Type check for Array literal with an Array of Arrays instance", () => { 435 | checkTypes([[54, 55], [34, 75]], [Array], err => { 436 | expect(err).toEqual(null) 437 | }) 438 | }) 439 | 440 | test("Type check for Array literal with an Array of Arrays with element type instance", () => { 441 | checkTypes([[54, 55], [34, "75"]], [[Number]], err => { 442 | expect(err).toEqual( 443 | expect.arrayContaining([ 444 | expect.objectContaining({ 445 | propertyName: "[1][1]", 446 | expectedType: "Number", 447 | receivedType: "String", 448 | required: false 449 | }) 450 | ]) 451 | ) 452 | }) 453 | }) 454 | 455 | test("Type check for Array literal with an Array of Object instance", () => { 456 | checkTypes([{ anObject: true }, { name: "john" }], [Object], err => { 457 | expect(err).toEqual(null) 458 | }) 459 | }) 460 | 461 | test("Type check for Array literal with an Array of Object literal", () => { 462 | checkTypes([{ name: "Adam" }, { name: false }], [{ name: String }], err => { 463 | expect(err).toEqual( 464 | expect.arrayContaining([ 465 | expect.objectContaining({ 466 | propertyName: "[1].name", 467 | expectedType: "String", 468 | receivedType: "Boolean", 469 | required: false 470 | }) 471 | ]) 472 | ) 473 | }) 474 | }) 475 | 476 | test("Type check for Array literal with an Array of Object with an Array property literal", () => { 477 | checkTypes( 478 | [ 479 | { name: "Adam", friends: ["john", "james"] }, 480 | { name: "john", friends: ["Adam", true] } 481 | ], 482 | [{ name: String, friends: [String] }], 483 | err => { 484 | expect(err).toEqual( 485 | expect.arrayContaining([ 486 | expect.objectContaining({ 487 | propertyName: "[1].friends[1]", 488 | expectedType: "String", 489 | receivedType: "Boolean", 490 | required: false 491 | }) 492 | ]) 493 | ) 494 | } 495 | ) 496 | }) 497 | 498 | /* Object Tests */ 499 | 500 | test("Type check for Object with Object instance", () => { 501 | checkTypes({ anObject: true }, Object, err => { 502 | expect(err).toEqual(null) 503 | }) 504 | }) 505 | 506 | test("Type check for Object with a Number", () => { 507 | checkTypes(65, Object, err => { 508 | expect(err).toEqual( 509 | expect.arrayContaining([ 510 | expect.objectContaining({ 511 | expectedType: "Object", 512 | receivedType: "Number", 513 | required: true 514 | }) 515 | ]) 516 | ) 517 | }) 518 | }) 519 | 520 | test("Type check for Object with a Boolean", () => { 521 | checkTypes(true, Object, err => { 522 | expect(err).toEqual( 523 | expect.arrayContaining([ 524 | expect.objectContaining({ 525 | expectedType: "Object", 526 | receivedType: "Boolean", 527 | required: true 528 | }) 529 | ]) 530 | ) 531 | }) 532 | }) 533 | 534 | test("Type check for Object with a String", () => { 535 | checkTypes('{ "aJsonObject": true }', Object, err => { 536 | expect(err).toEqual( 537 | expect.arrayContaining([ 538 | expect.objectContaining({ 539 | expectedType: "Object", 540 | receivedType: "String", 541 | required: true 542 | }) 543 | ]) 544 | ) 545 | }) 546 | }) 547 | 548 | test("Type check for Object with a Symbol", () => { 549 | checkTypes(Symbol({ anObject: true }), Object, err => { 550 | expect(err).toEqual( 551 | expect.arrayContaining([ 552 | expect.objectContaining({ 553 | expectedType: "Object", 554 | receivedType: "Symbol", 555 | required: true 556 | }) 557 | ]) 558 | ) 559 | }) 560 | }) 561 | 562 | test("Type check for Object with a Array", () => { 563 | checkTypes([{ anObject: true }], Object, err => { 564 | expect(err).toEqual( 565 | expect.arrayContaining([ 566 | expect.objectContaining({ 567 | expectedType: "Object", 568 | receivedType: "Array", 569 | required: true 570 | }) 571 | ]) 572 | ) 573 | }) 574 | }) 575 | 576 | /* Object Literal Tests */ 577 | 578 | test("Type check for Object with Object literal", () => { 579 | checkTypes({ anObject: true }, {}, err => { 580 | expect(err).toEqual(null) 581 | }) 582 | }) 583 | 584 | test("Type check for Object with an Object literal with scheme", () => { 585 | checkTypes( 586 | { enabled: true, age: 43, name: 0 }, 587 | { enabled: Boolean, age: Number, name: String }, 588 | err => { 589 | expect(err).toEqual( 590 | expect.arrayContaining([ 591 | expect.objectContaining({ 592 | propertyName: "name", 593 | expectedType: "String", 594 | receivedType: "Number", 595 | required: false 596 | }) 597 | ]) 598 | ) 599 | } 600 | ) 601 | }) 602 | 603 | test("Type check for Object with an Object literal with scheme (required fields)", () => { 604 | checkTypes( 605 | { enabled: true, age: 43, name: undefined }, 606 | { enabled: Boolean, age: Number, name: String, $required: ["name"] }, 607 | err => { 608 | expect(err).toEqual( 609 | expect.arrayContaining([ 610 | expect.objectContaining({ 611 | propertyName: "name", 612 | expectedType: "String", 613 | receivedType: "None", 614 | required: true 615 | }) 616 | ]) 617 | ) 618 | } 619 | ) 620 | }) 621 | 622 | test("Type check for Object with an Object literal with scheme (required fields and includes a null)", () => { 623 | checkTypes( 624 | { enabled: true, age: 43, name: undefined, address: null }, 625 | { enabled: Boolean, age: Number, name: String, $required: ["name"] }, 626 | err => { 627 | expect(err).toEqual( 628 | expect.arrayContaining([ 629 | expect.objectContaining({ 630 | propertyName: "name", 631 | expectedType: "String", 632 | receivedType: "None", 633 | required: true 634 | }) 635 | ]) 636 | ) 637 | } 638 | ) 639 | }) 640 | 641 | test("Type check for Object with an Object literal with a property Object scheme (all required fields)", () => { 642 | checkTypes( 643 | { 644 | enabled: true, 645 | age: 43, 646 | name: "john", 647 | address: { 648 | street: "123 Main St", 649 | city: "Boston", 650 | zipCode: "01124" 651 | } 652 | }, 653 | { 654 | enabled: Boolean, 655 | age: Number, 656 | name: String, 657 | address: { 658 | street: String, 659 | city: String, 660 | state: String, 661 | zipCode: String, 662 | $required 663 | }, 664 | $required: ["name"] 665 | }, 666 | err => { 667 | expect(err).toEqual( 668 | expect.arrayContaining([ 669 | expect.objectContaining({ 670 | propertyName: "address.state", 671 | expectedType: "String", 672 | receivedType: "None", 673 | required: true 674 | }) 675 | ]) 676 | ) 677 | } 678 | ) 679 | }) 680 | 681 | test("Type check for Object with an Object literal with a property Object scheme with an Array property", () => { 682 | checkTypes( 683 | { 684 | enabled: true, 685 | age: 43, 686 | name: "john", 687 | address: { 688 | street: "123 Main St", 689 | city: "Boston", 690 | zipCode: "01124" 691 | }, 692 | purchases: [ 693 | { item: "purse", price: 45.0 }, 694 | { item: "jacket", price: "65.00" } 695 | ] 696 | }, 697 | { 698 | enabled: Boolean, 699 | age: Number, 700 | name: String, 701 | address: { 702 | street: String, 703 | city: String, 704 | state: String, 705 | zipCode: String, 706 | $required 707 | }, 708 | purchases: [{ item: String, price: Number }], 709 | $required: ["name"] 710 | }, 711 | err => { 712 | expect(err).toEqual( 713 | expect.arrayContaining([ 714 | expect.objectContaining({ 715 | propertyName: "address.state", 716 | expectedType: "String", 717 | receivedType: "None", 718 | required: true 719 | }), 720 | expect.objectContaining({ 721 | propertyName: "purchases[1].price", 722 | expectedType: "Number", 723 | receivedType: "String", 724 | required: false 725 | }) 726 | ]) 727 | ) 728 | } 729 | ) 730 | }) 731 | 732 | /* getVal() Tests */ 733 | 734 | test("getVal() check for getting correct value from an Object", () => { 735 | checkTypes( 736 | { 737 | enabled: true, 738 | age: "43", 739 | name: "john" 740 | }, 741 | { 742 | enabled: Boolean, 743 | age: Number, 744 | name: String 745 | }, 746 | (err, passedObject) => { 747 | expect(err).toEqual( 748 | expect.arrayContaining([ 749 | expect.objectContaining({ 750 | propertyName: "age", 751 | expectedType: "Number", 752 | receivedType: "String", 753 | required: false 754 | }) 755 | ]) 756 | ) 757 | 758 | const value = getVal(passedObject, err[0].propertyName) 759 | expect(value).toEqual("43") 760 | } 761 | ) 762 | }) 763 | 764 | test("getVal() check for getting correct value from an Object within an Object", () => { 765 | checkTypes( 766 | { 767 | enabled: true, 768 | age: 43, 769 | name: "john", 770 | address: { 771 | street: "123 Main St", 772 | city: "Boston", 773 | state: "MA", 774 | zipCode: 11240 775 | } 776 | }, 777 | { 778 | enabled: Boolean, 779 | age: Number, 780 | name: String, 781 | address: { 782 | street: String, 783 | city: String, 784 | state: String, 785 | zipCode: String, 786 | $required 787 | }, 788 | $required: ["name"] 789 | }, 790 | (err, passedObject) => { 791 | expect(err).toEqual( 792 | expect.arrayContaining([ 793 | expect.objectContaining({ 794 | propertyName: "address.zipCode", 795 | expectedType: "String", 796 | receivedType: "Number", 797 | required: true 798 | }) 799 | ]) 800 | ) 801 | 802 | const value = getVal(passedObject, err[0].propertyName) 803 | expect(value).toEqual(11240) 804 | } 805 | ) 806 | }) 807 | 808 | test("getVal() check for getting correct value from an Object within an Array within an Object", () => { 809 | checkTypes( 810 | { 811 | enabled: true, 812 | age: 43, 813 | name: "john", 814 | address: { 815 | street: "123 Main St", 816 | city: "Boston", 817 | zipCode: "01124" 818 | }, 819 | purchases: [ 820 | { item: true, price: 45.0 }, 821 | { item: "jacket", price: "65.00" } 822 | ] 823 | }, 824 | { 825 | enabled: Boolean, 826 | age: Number, 827 | name: String, 828 | address: { 829 | street: String, 830 | city: String, 831 | state: String, 832 | zipCode: String, 833 | $required 834 | }, 835 | purchases: [{ item: String, price: Number }], 836 | $required: ["name"] 837 | }, 838 | (err, passedObject) => { 839 | expect(err).toEqual( 840 | expect.arrayContaining([ 841 | expect.objectContaining({ 842 | propertyName: "address.state", 843 | expectedType: "String", 844 | receivedType: "None", 845 | required: true 846 | }), 847 | expect.objectContaining({ 848 | propertyName: "purchases[0].item", 849 | expectedType: "String", 850 | receivedType: "Boolean", 851 | required: false 852 | }), 853 | expect.objectContaining({ 854 | propertyName: "purchases[1].price", 855 | expectedType: "Number", 856 | receivedType: "String", 857 | required: false 858 | }) 859 | ]) 860 | ) 861 | 862 | const value = getVal(passedObject, err[2].propertyName) 863 | const value2 = getVal(passedObject, err[1].propertyName) 864 | 865 | expect(value).toEqual("65.00") 866 | expect(value2).toEqual(true) 867 | } 868 | ) 869 | }) 870 | 871 | test("getVal() check for getting correct value from within an Array", () => { 872 | checkTypes([55, 654, "45"], [Number], (err, passedObject) => { 873 | expect(err).toEqual( 874 | expect.arrayContaining([ 875 | expect.objectContaining({ 876 | propertyName: "[2]", 877 | expectedType: "Number", 878 | receivedType: "String", 879 | required: false 880 | }) 881 | ]) 882 | ) 883 | 884 | const value = getVal(passedObject, err[0].propertyName) 885 | expect(value).toEqual("45") 886 | }) 887 | }) 888 | 889 | test("getVal() check for getting correct value from an Array within an Array", () => { 890 | checkTypes( 891 | [[554, 656, 65], [43, 66, "33"]], 892 | [[Number]], 893 | (err, passedObject) => { 894 | expect(err).toEqual( 895 | expect.arrayContaining([ 896 | expect.objectContaining({ 897 | propertyName: "[1][2]", 898 | expectedType: "Number", 899 | receivedType: "String", 900 | required: false 901 | }) 902 | ]) 903 | ) 904 | 905 | const value = getVal(passedObject, err[0].propertyName) 906 | expect(value).toEqual("33") 907 | } 908 | ) 909 | }) 910 | 911 | test("getVal() check for getting correct value from an Object within an Array", () => { 912 | checkTypes( 913 | [ 914 | { 915 | enabled: true, 916 | age: 55, 917 | name: "Adam" 918 | }, 919 | { 920 | enabled: true, 921 | age: "43", 922 | name: "John" 923 | } 924 | ], 925 | [ 926 | { 927 | enabled: Boolean, 928 | age: Number, 929 | name: String, 930 | $required: ["name", "age"] 931 | } 932 | ], 933 | (err, passedObject) => { 934 | expect(err).toEqual( 935 | expect.arrayContaining([ 936 | expect.objectContaining({ 937 | propertyName: "[1].age", 938 | expectedType: "Number", 939 | receivedType: "String", 940 | required: true 941 | }) 942 | ]) 943 | ) 944 | 945 | const value = getVal(passedObject, err[0].propertyName) 946 | expect(value).toEqual("43") 947 | } 948 | ) 949 | }) 950 | 951 | test("getVal() check for getting correct value from an Array within an Object within an Array", () => { 952 | checkTypes( 953 | [ 954 | { 955 | enabled: true, 956 | age: 55, 957 | name: "Adam", 958 | friends: ["adam", "hannah"] 959 | }, 960 | { 961 | enabled: true, 962 | age: 43, 963 | name: "John", 964 | friends: ["james", 45] 965 | } 966 | ], 967 | [ 968 | { 969 | enabled: Boolean, 970 | age: Number, 971 | name: String, 972 | friends: [String], 973 | $required: ["name", "age"] 974 | } 975 | ], 976 | (err, passedObject) => { 977 | expect(err).toEqual( 978 | expect.arrayContaining([ 979 | expect.objectContaining({ 980 | propertyName: "[1].friends[1]", 981 | expectedType: "String", 982 | receivedType: "Number", 983 | required: false 984 | }) 985 | ]) 986 | ) 987 | 988 | const value = getVal(passedObject, err[0].propertyName) 989 | expect(value).toEqual(45) 990 | } 991 | ) 992 | }) 993 | 994 | /* setVal() Tests */ 995 | 996 | test("setVal() check for setting correct value in an Object", () => { 997 | checkTypes( 998 | { 999 | enabled: true, 1000 | age: "43", 1001 | name: "john" 1002 | }, 1003 | { 1004 | enabled: Boolean, 1005 | age: Number, 1006 | name: String 1007 | }, 1008 | (err, passedObject) => { 1009 | expect(err).toEqual( 1010 | expect.arrayContaining([ 1011 | expect.objectContaining({ 1012 | propertyName: "age", 1013 | expectedType: "Number", 1014 | receivedType: "String", 1015 | required: false 1016 | }) 1017 | ]) 1018 | ) 1019 | 1020 | const value = getVal(passedObject, err[0].propertyName) 1021 | expect(value).toEqual("43") 1022 | 1023 | setVal(passedObject, err[0].propertyName, parseInt(value)) 1024 | const changedVal = getVal(passedObject, err[0].propertyName) 1025 | expect(changedVal).toEqual(43) 1026 | } 1027 | ) 1028 | }) 1029 | 1030 | test("SetVal() check for setting correct value in an Object within an Object", () => { 1031 | checkTypes( 1032 | { 1033 | enabled: true, 1034 | age: 43, 1035 | name: "john", 1036 | address: { 1037 | street: "123 Main St", 1038 | city: "Boston", 1039 | state: "MA", 1040 | zipCode: 11240 1041 | } 1042 | }, 1043 | { 1044 | enabled: Boolean, 1045 | age: Number, 1046 | name: String, 1047 | address: { 1048 | street: String, 1049 | city: String, 1050 | state: String, 1051 | zipCode: String, 1052 | $required 1053 | }, 1054 | $required: ["name"] 1055 | }, 1056 | (err, passedObject) => { 1057 | expect(err).toEqual( 1058 | expect.arrayContaining([ 1059 | expect.objectContaining({ 1060 | propertyName: "address.zipCode", 1061 | expectedType: "String", 1062 | receivedType: "Number", 1063 | required: true 1064 | }) 1065 | ]) 1066 | ) 1067 | 1068 | const value = getVal(passedObject, err[0].propertyName) 1069 | expect(value).toEqual(11240) 1070 | 1071 | setVal(passedObject, err[0].propertyName, value.toString()) 1072 | const changedVal = getVal(passedObject, err[0].propertyName) 1073 | expect(changedVal).toEqual("11240") 1074 | } 1075 | ) 1076 | }) 1077 | 1078 | test("SetVal() check for setting correct value in an Object within an Array within an Object", () => { 1079 | checkTypes( 1080 | { 1081 | enabled: true, 1082 | age: 43, 1083 | name: "john", 1084 | address: { 1085 | street: "123 Main St", 1086 | city: "Boston", 1087 | zipCode: "01124" 1088 | }, 1089 | purchases: [ 1090 | { item: true, price: 45.0 }, 1091 | { item: "jacket", price: "65.00" } 1092 | ] 1093 | }, 1094 | { 1095 | enabled: Boolean, 1096 | age: Number, 1097 | name: String, 1098 | address: { 1099 | street: String, 1100 | city: String, 1101 | state: String, 1102 | zipCode: String, 1103 | $required 1104 | }, 1105 | purchases: [{ item: String, price: Number }], 1106 | $required: ["name"] 1107 | }, 1108 | (err, passedObject) => { 1109 | expect(err).toEqual( 1110 | expect.arrayContaining([ 1111 | expect.objectContaining({ 1112 | propertyName: "address.state", 1113 | expectedType: "String", 1114 | receivedType: "None", 1115 | required: true 1116 | }), 1117 | expect.objectContaining({ 1118 | propertyName: "purchases[0].item", 1119 | expectedType: "String", 1120 | receivedType: "Boolean", 1121 | required: false 1122 | }), 1123 | expect.objectContaining({ 1124 | propertyName: "purchases[1].price", 1125 | expectedType: "Number", 1126 | receivedType: "String", 1127 | required: false 1128 | }) 1129 | ]) 1130 | ) 1131 | 1132 | const value = getVal(passedObject, err[2].propertyName) 1133 | const value2 = getVal(passedObject, err[1].propertyName) 1134 | 1135 | expect(value).toEqual("65.00") 1136 | expect(value2).toEqual(true) 1137 | 1138 | setVal(passedObject, err[2].propertyName, parseInt(value)) 1139 | const changedVal = getVal(passedObject, err[2].propertyName) 1140 | expect(changedVal).toEqual(65) 1141 | } 1142 | ) 1143 | }) 1144 | 1145 | test("setVal() check for setting correct value within an Array", () => { 1146 | checkTypes([55, 654, "45"], [Number], (err, passedObject) => { 1147 | expect(err).toEqual( 1148 | expect.arrayContaining([ 1149 | expect.objectContaining({ 1150 | propertyName: "[2]", 1151 | expectedType: "Number", 1152 | receivedType: "String", 1153 | required: false 1154 | }) 1155 | ]) 1156 | ) 1157 | 1158 | const value = getVal(passedObject, err[0].propertyName) 1159 | expect(value).toEqual("45") 1160 | 1161 | setVal(passedObject, err[0].propertyName, parseInt(value)) 1162 | const changedVal = getVal(passedObject, err[0].propertyName) 1163 | expect(changedVal).toEqual(45) 1164 | }) 1165 | }) 1166 | 1167 | test("setVal() check for setting correct value in an Array within an Array", () => { 1168 | checkTypes( 1169 | [[554, 656, 65], [43, 66, "33"]], 1170 | [[Number]], 1171 | (err, passedObject) => { 1172 | expect(err).toEqual( 1173 | expect.arrayContaining([ 1174 | expect.objectContaining({ 1175 | propertyName: "[1][2]", 1176 | expectedType: "Number", 1177 | receivedType: "String", 1178 | required: false 1179 | }) 1180 | ]) 1181 | ) 1182 | 1183 | const value = getVal(passedObject, err[0].propertyName) 1184 | expect(value).toEqual("33") 1185 | 1186 | setVal(passedObject, err[0].propertyName, parseInt(value)) 1187 | 1188 | const changedVal = getVal(passedObject, err[0].propertyName) 1189 | expect(changedVal).toEqual(33) 1190 | } 1191 | ) 1192 | }) 1193 | 1194 | test("setVal() check for setting correct value in an Array within an Array", () => { 1195 | checkTypes( 1196 | [[554, 656, 65], [43, 66, "33"]], 1197 | [[Number]], 1198 | (err, passedObject) => { 1199 | expect(err).toEqual( 1200 | expect.arrayContaining([ 1201 | expect.objectContaining({ 1202 | propertyName: "[1][2]", 1203 | expectedType: "Number", 1204 | receivedType: "String", 1205 | required: false 1206 | }) 1207 | ]) 1208 | ) 1209 | 1210 | const value = getVal(passedObject, err[0].propertyName) 1211 | expect(value).toEqual("33") 1212 | 1213 | setVal(passedObject, err[0].propertyName, parseInt(value)) 1214 | 1215 | const changedVal = getVal(passedObject, err[0].propertyName) 1216 | expect(changedVal).toEqual(33) 1217 | } 1218 | ) 1219 | }) 1220 | 1221 | test("setVal() check for getting correct value in an Object within an Array", () => { 1222 | checkTypes( 1223 | [ 1224 | { 1225 | enabled: true, 1226 | age: 55, 1227 | name: "Adam" 1228 | }, 1229 | { 1230 | enabled: true, 1231 | age: "43", 1232 | name: "John" 1233 | } 1234 | ], 1235 | [ 1236 | { 1237 | enabled: Boolean, 1238 | age: Number, 1239 | name: String, 1240 | $required: ["name", "age"] 1241 | } 1242 | ], 1243 | (err, passedObject) => { 1244 | expect(err).toEqual( 1245 | expect.arrayContaining([ 1246 | expect.objectContaining({ 1247 | propertyName: "[1].age", 1248 | expectedType: "Number", 1249 | receivedType: "String", 1250 | required: true 1251 | }) 1252 | ]) 1253 | ) 1254 | 1255 | const value = getVal(passedObject, err[0].propertyName) 1256 | expect(value).toEqual("43") 1257 | 1258 | setVal(passedObject, err[0].propertyName, parseInt(value)) 1259 | const changedVal = getVal(passedObject, err[0].propertyName) 1260 | expect(changedVal).toEqual(43) 1261 | } 1262 | ) 1263 | }) 1264 | 1265 | test("setVal() + getVal() check for setting correct value in an Object within an Array with stepBack setting", () => { 1266 | checkTypes( 1267 | [ 1268 | { 1269 | enabled: true, 1270 | age: 55, 1271 | name: "Adam", 1272 | address: { 1273 | street: "123 Main st", 1274 | state: "MA", 1275 | zipCode: 34535 1276 | } 1277 | }, 1278 | { 1279 | enabled: true, 1280 | age: 43, 1281 | name: "John", 1282 | address: { 1283 | street: "245 State st", 1284 | state: "CA", 1285 | zipCode: "90210" 1286 | } 1287 | } 1288 | ], 1289 | [ 1290 | { 1291 | enabled: Boolean, 1292 | age: Number, 1293 | name: String, 1294 | address: { 1295 | street: String, 1296 | state: String, 1297 | zipCode: Number 1298 | }, 1299 | $required: ["name", "age"] 1300 | } 1301 | ], 1302 | (err, passedObject) => { 1303 | expect(err).toEqual( 1304 | expect.arrayContaining([ 1305 | expect.objectContaining({ 1306 | propertyName: "[1].address.zipCode", 1307 | expectedType: "Number", 1308 | receivedType: "String", 1309 | required: false 1310 | }) 1311 | ]) 1312 | ) 1313 | 1314 | const value = getVal(passedObject, err[0].propertyName, 2) 1315 | expect(value).toEqual( 1316 | expect.objectContaining({ 1317 | enabled: true, 1318 | age: 43, 1319 | name: "John", 1320 | address: { 1321 | street: "245 State st", 1322 | state: "CA", 1323 | zipCode: "90210" 1324 | } 1325 | }) 1326 | ) 1327 | 1328 | setVal( 1329 | passedObject, 1330 | err[0].propertyName, 1331 | { 1332 | enabled: true, 1333 | age: 43, 1334 | name: "John", 1335 | address: { 1336 | street: "245 State st", 1337 | state: "CA", 1338 | zipCode: 90210 1339 | } 1340 | }, 1341 | 2 1342 | ) 1343 | 1344 | const changedVal = getVal(passedObject, err[0].propertyName, 1) 1345 | expect(changedVal).toEqual( 1346 | expect.objectContaining({ 1347 | street: "245 State st", 1348 | state: "CA", 1349 | zipCode: 90210 1350 | }) 1351 | ) 1352 | } 1353 | ) 1354 | }) 1355 | 1356 | test("setVal() + getVal() check for setting correct value in a nested Object with stepBack setting", () => { 1357 | checkTypes( 1358 | { 1359 | a: { 1360 | b: { 1361 | c: { 1362 | d: { 1363 | e: { 1364 | val: [true] 1365 | } 1366 | } 1367 | } 1368 | } 1369 | } 1370 | }, 1371 | { 1372 | a: { 1373 | b: { 1374 | c: { 1375 | d: { 1376 | e: { 1377 | val: [Number] 1378 | } 1379 | } 1380 | } 1381 | } 1382 | } 1383 | }, 1384 | (err, passedObject) => { 1385 | const value = getVal(passedObject, err[0].propertyName, 4) 1386 | expect(value).toEqual( 1387 | expect.objectContaining({ 1388 | d: { 1389 | e: { 1390 | val: [true] 1391 | } 1392 | } 1393 | }) 1394 | ) 1395 | 1396 | const props = propsFromString(err[0].propertyName) 1397 | expect(props.length).toEqual(7) 1398 | 1399 | 1400 | const changedValObj = setVal( 1401 | passedObject, 1402 | err[0].propertyName, 1403 | { 1404 | c: { 1405 | d: { 1406 | e: { 1407 | val: [5] 1408 | } 1409 | } 1410 | } 1411 | }, 1412 | 5 1413 | ) 1414 | 1415 | expect(changedValObj).toEqual( 1416 | expect.objectContaining({ 1417 | c: { 1418 | d: { 1419 | e: { 1420 | val: [5] 1421 | } 1422 | } 1423 | } 1424 | }) 1425 | ) 1426 | 1427 | const changedVal = getVal(passedObject, err[0].propertyName, 4) 1428 | expect(changedVal).toEqual( 1429 | expect.objectContaining({ 1430 | d: { 1431 | e: { 1432 | val: [5] 1433 | } 1434 | } 1435 | }) 1436 | ) 1437 | } 1438 | ) 1439 | }) 1440 | 1441 | test('getType() returns proper types', () => { 1442 | expect(getType(2)).toEqual('Number') 1443 | expect(getType(true)).toEqual('Boolean') 1444 | expect(getType('a string')).toEqual('String') 1445 | expect(getType([])).toEqual('Array') 1446 | expect(getType({})).toEqual('Object') 1447 | expect(getType(Symbol(34))).toEqual('Symbol') 1448 | expect(getType(null)).toEqual('Null') 1449 | expect(getType(undefined)).toEqual('Undefined') 1450 | }) 1451 | 1452 | 1453 | test('stringToType() returns proper types', ()=>{ 1454 | expect(stringToType('String')).toEqual(String) 1455 | expect(stringToType('Array')).toEqual(Array) 1456 | expect(stringToType('Object')).toEqual(Object) 1457 | expect(stringToType('Number')).toEqual(Number) 1458 | expect(stringToType('Undefined')).toEqual(undefined) 1459 | expect(stringToType('Null')).toEqual(null) 1460 | expect(stringToType('Symbol')).toEqual(Symbol) 1461 | expect(stringToType('Boolean')).toEqual(Boolean) 1462 | }) 1463 | 1464 | test('typeToString() returns proper strings', ()=>{ 1465 | expect(typeToString(String)).toEqual('String') 1466 | expect(typeToString(Array)).toEqual('Array') 1467 | expect(typeToString(Object)).toEqual('Object') 1468 | expect(typeToString(Number)).toEqual('Number') 1469 | expect(typeToString(null)).toEqual('Null') 1470 | expect(typeToString(undefined)).toEqual('Undefined') 1471 | expect(typeToString(Symbol)).toEqual('Symbol') 1472 | expect(typeToString(Boolean)).toEqual('Boolean') 1473 | }) 1474 | 1475 | --------------------------------------------------------------------------------