UNPKG

typeanalyser

Version:

Provides type detection and analysis for ANY Javascript object (including custom types) in any environment, even where native operators falter.

1 lines 27.5 kB
{"version":3,"file":"type-analyser.cjs.min","sources":["../src/getTypeOf.js","../src/getNumTypeOf.js","../src/misc.js"],"sourcesContent":["/*******************************************************************************************************\r\n * Type-Analyser\r\n * MIT License\r\n * For full license details see the LICENSE file in the repo root or https://opensource.org/licenses/MIT\r\n * Copyright(c) 2023 Owen Cullum <dev@metagu.com>\r\n *******************************************************************************************************/\r\n\r\n/**\r\n * Accurately identifies the type of all Javascript objects not just the primitive types and it's much more\r\n * useful than the built-in javascript 'typeof' operator. It provides the same core functionality but also \r\n * has the following advantages. It; \r\n * \r\n * - returns the correct type for null, Array, **all** ES6 / ES2020 types and custom types ( E.g. your classes ). \r\n * \r\n * - works correctly with types correctly simulated via Polyfills (E.g. Symbol via Babel ) \r\n * \r\n * - distinquishes between different types of functions( regular, async, generator, arrow ) \r\n * \r\n * - correctly identifies types retieved from Iframes and Worker threads where passing of those types is supported.\r\n * \r\n * \r\n * SPECIAL CASES - toString( ) Override and [Symbol.toStringTag]\r\n * There are some special cases where the returned 'type' might not align with your expectations:\r\n * \r\n * Objects with toString() Overridden: If the built-in `toString()` method is overridden by custom code, 'unknown'\r\n * will be returned for the type. You can still use `toString()` to retrieve \r\n * the value set by the custom code.\r\n * \r\n * Custom Classes with [Symbol.toStringTag]: If custom classes have `[Symbol.toStringTag]` set, the returned value will be\r\n * the class name rather than the toStringTag value. This design is intentional. \r\n * If you want to retrieve the custom tag, [Symbol.toStringTag] will still return \r\n * whatever value was set.\r\n *\r\n * Object Types with [Symbol.toStringTag]: Actual Object types with [Symbol.toStringTag] set will have that value \r\n * returned for the type of the object.\r\n *\r\n * Rationale\r\n * The goal here is to reveal the intrinsic underlying 'type' of an object. For built-in types and custom objects, using\r\n * [Symbol.toStringTag] doesn't alter that. However, we consider actual Object types an exception and return the \r\n * [Symbol.toStringTag] value. This is because JavaScript's type system returns 'object' for all Objects, making it \r\n * impossible to distinguish one type of custom Object from another without using [Symbol.toStringTag].\r\n * \r\n * @param {*} obj - The object to get the type of.\r\n * \r\n * @returns a string representing the type of the object passed in. if a type can't be determined, the string 'unknown' \r\n * will be returned. The following types will be in lower case as per the built-in javascript typeof operator:\r\n * 'string', 'number', 'boolean', 'undefined', 'symbol', 'function', 'object', 'bigint'. All other built-in\r\n * types will be recognised and returned in CamelCase format as per the Javascript standard: E.g. 'Array', \r\n * 'Date', 'Error', 'RegExp', 'URL' etc.\r\n */\r\n\r\nfunction getTypeOf(obj) {\r\n if (obj === null) return 'null';\r\n\r\n var typeStr = typeof(obj);\r\n var basicType = typeStr;\r\n\r\n if (typeStr !== 'object' && typeStr !== 'function' && typeStr !== 'string') {\r\n return typeStr;\r\n }\r\n\r\n // if the object has the toString method overridden, then we can't accurately determine it's type\r\n if ( obj.hasOwnProperty(\"toString\") ) {\r\n return \"unknown\"\r\n }\r\n\r\n // special case to handle old ES5 Symbol Polyfills which should always have a value of Symbol(something)\r\n if ( typeStr === 'string' ) {\r\n return (obj.valueOf && obj.valueOf().toString().slice(0,6) === 'Symbol') ? 'symbol' : 'string';\r\n }\r\n\r\n // get a more detailed string representation of the object's type\r\n // slice(8, -1) removes the constant '[object' and the last ']' parts of the string\r\n typeStr = Object.prototype.toString.call(obj).slice(8, -1);\r\n\r\n if (!obj.prototype && typeStr === 'Function') { \r\n return 'ArrowFunction';\r\n } \r\n\r\n if ( typeStr === 'Object' || typeStr === 'Function' ) {\r\n typeStr = typeStr.toLowerCase();\r\n }\r\n\r\n if ( basicType === 'object' && obj.constructor ) {\r\n var es6ClassName = obj.constructor.name;\r\n if (es6ClassName !== 'Object') {\r\n return typeStr = es6ClassName;\r\n } \r\n } \r\n\r\n return typeStr;\r\n}\r\n\r\n/**\r\n * Performs type introspection and returns detailed type information about the object passed in. This function returns \r\n * useful information about all types including ES6 / EES2020 and customs types ( E.g. Your classes ). \r\n * \r\n * For the returned 'type' field, the same special cases involving the use of `toString( )` override and `[Symbol.toStringTag]`\r\n * apply as per the **`getTypeOf`** function above and the same rationale applies. The goal is for the 'type' field\r\n * to reveal the intrinsic underlying 'type' of an object. For built-in types and custom objects, using `[Symbol.toStringTag]`\r\n * doesn't alter that by design here. However, we consider actual `Object` types an exception and return the \r\n * `[Symbol.toStringTag]` value. This is because JavaScript's type system returns 'object' for all Objects, making it \r\n * impossible to distinguish one type of custom Object from another without using `[Symbol.toStringTag]`.\r\n * \r\n * @param {*} obj - the object to get type information about.\r\n * \r\n * @param {*} showFullPrototypeChain - Optional. if true (the default value), the full javascript inheritance prototype chain will be included \r\n * in the returned object. \r\n * if false, The final 'Object' will be removed from the chain and also only \r\n * chains longer than 1 will be included as in this case the chain will be just \r\n * have a single value the same as the Type field which is not very useful.\r\n * \r\n * @returns - an object containing the following fields (Default values are shown):\r\n * { Type: \"null\", // A string representation of the exact input type. This is set for all types not just primitives. \r\n * The following types will be in lower case as per the built-in javascript typeof operator: \r\n * 'string', 'number', 'boolean', 'undefined', 'symbol', 'function', 'object', 'bigint'.\r\n * A Null object will be detected as 'null'. All other built-in types will be recognised and returned\r\n * in CamelCase Format as per the Javascirpt standard: E.g. 'Array', 'Date', 'Error', 'RegExp', 'URL' etc\r\n * if a type can't be determined, then 'unknown' will be returned.\r\n * ReferenceVariable: \"\", // A string representation of the reference variable, if any, that points to the input object. \r\n * hasCustomConstructor: false, // true if the input object has a it's own custom constructor, false otherwise.\r\n * prototypeChainString : \"\", // a string representation of the Javascript inheritance prototype chain of the input object. Objects\r\n * in the chain are separated by ' -> '. E.g. 'Child -> Parent -> Object'. \r\n * prototypeChain : null, // an array containing the javascript inheritance prototype chain of the input object passed.\r\n * };\r\n */\r\nfunction getTypeDetails(obj, showFullPrototypeChain) {\r\n\r\n showFullPrototypeChain = showFullPrototypeChain === undefined ? true : showFullPrototypeChain; \r\n\r\n var resultInfo = { Type: \"null\", \r\n ReferenceVariable: \"\",\r\n hasCustomConstructor : false,\r\n prototypeChainString : \"\",\r\n prototypeChain : null,\r\n };\r\n\r\n if (obj === null) { \r\n return resultInfo;\r\n } \r\n if (obj === undefined) {\r\n resultInfo.Type = 'undefined';\r\n return resultInfo;\r\n } \r\n\r\n var coreTypes = ['String', 'Number', 'Boolean', 'Undefined', 'Null', 'Symbol', 'Function', 'Object', 'BigInt'];\r\n\r\n var typeStr = Object.prototype.toString.call(obj).slice(8, -1);\r\n if ( coreTypes.includes(typeStr) ) {\r\n typeStr = typeStr.toLowerCase();\r\n }\r\n \r\n if (!obj.prototype && typeStr === 'function') { \r\n typeStr = 'ArrowFunction';\r\n }\r\n\r\n var es6ClassName;\r\n if ( typeof obj === 'object' && obj.constructor) {\r\n es6ClassName = obj.constructor.name;\r\n if (es6ClassName !== 'Object') {\r\n typeStr = es6ClassName;\r\n resultInfo.hasCustomConstructor = true;\r\n }\r\n } \r\n\r\n // if the object has the toString method overridden, then we can't accurately determine it's type\r\n if ( obj.hasOwnProperty(\"toString\") ) {\r\n typeStr = \"unknownn\";\r\n } \r\n\r\n var pChain = getPrototypeChain(obj);\r\n if ( showFullPrototypeChain ) {\r\n resultInfo.prototypeChain = pChain;\r\n resultInfo.prototypeChainString = pChain.join(' -> ');\r\n } else {\r\n // remove the last element which is always Object and only show chains longer than 1\r\n pChain.pop();\r\n if ( pChain.length > 1 ) { \r\n resultInfo.prototypeChain = pChain;\r\n resultInfo.prototypeChainString = pChain.join(' -> ');\r\n }\r\n }\r\n\r\n resultInfo.Type = typeStr;\r\n if (obj.name && !es6ClassName) {\r\n if (Object.prototype.hasOwnProperty.call(obj, 'name')) {\r\n resultInfo.ReferenceVariable = obj.name;\r\n } \r\n }\r\n\r\n return resultInfo;\r\n}\r\n\r\n/**\r\n * get the full prototype chain of the object passed in.\r\n * @param {*} obj \r\n * @returns an array containing the names of the objects in the prototype chain of the object passed in.\r\n */\r\nfunction getPrototypeChain(obj) {\r\n var proto = Object.getPrototypeOf(obj);\r\n var chain = [];\r\n\r\n while (proto != null) {\r\n chain.push(proto.constructor.name);\r\n proto = Object.getPrototypeOf(proto);\r\n }\r\n \r\n return chain;\r\n}\r\n\r\nfunction enhancedTypeOf() {\r\n console.warn('Warning: enhancedTypeOf is deprecated. Please use getTypeOf.');\r\n return getTypeOf.apply(this, arguments);\r\n }\r\n\r\nexport { getTypeDetails };\r\nexport { getTypeOf };\r\nexport { enhancedTypeOf };","/*******************************************************************************************************\r\n * Type-Analyser\r\n * MIT License\r\n * For full license details see the LICENSE file in the repo root or https://opensource.org/licenses/MIT\r\n * Copyright(c) 2023 Owen Cullum <dev@metagu.com>\r\n *******************************************************************************************************/\r\n \r\n/**\r\n * Returns information about the 'sub-type' of number passed in. Unlike the built-in javascript isNaN( ) function,\r\n * this returns useful information about the 'type' of number passed in. E.g. it will return 'infinity' for Infinity\r\n * or it will return 'unsafeNumber' for a number that is too large to be a safe integer. It will return 'NaN'\r\n * for BigInt and Symbol types unlike the built-in isNaN( ) function which throws a type error for these types.\r\n * \r\n * Also note \r\n * 1. NaN returns 'NaN' as that is the 'number' sub-type that it actually is.\r\n * 2. Booleans also returns 'NaN' because even though javascript coerces them into 0 or 1, relying on this\r\n * behaviour is not good practice and can lead to bugs.\r\n * 3. Number Objects (created via new Number(xxx)) return 'numberObject'. Some JavaScript methods\r\n * and functions behave differently when they receive an object instead of a primitive value. \r\n * E.g. when comparing with ===, new Number(10) !== 10.\r\n * \r\n * @param {*} obj the object to get number type information about.\r\n * \r\n * @param {*} acceptStringNumbers - Optional. if true (the default value), then if a string is passed in, it\r\n * will be converted to a number and the that will tested. Strings\r\n * are not coerceced to numbers if they do not represent a valid number. E.g '34.345abchs'\r\n * will not be converted to a number and will return 'NaN'. But '34.345' will be converted\r\n * to a number and will return 'safeFloat'. Strings representing Hex numbers also work - E.g. 0xFF. \r\n * (Note - the built in javascript parseFloat() function can be used before calling this \r\n * function to force coercing. E.g. it will convert '34.345abchs' to 34.345). \r\n * if acceptStringNumbers is false then when a string is passed in, it will never be \r\n * converted to a number and 'NaN' will be returned.\r\n * \r\n * @returns - a string representing the sub-type of the number passed in. The possible values are:\r\n * 'bigint', 'NaN' , 'infinity', '-infinity', 'safeInteger', 'unsafeNumber' 'safeFloat' and 'numberObject'\r\n */\r\nfunction getNumTypeOf (obj, acceptStringNumbers) {\r\n\r\n acceptStringNumbers = acceptStringNumbers === undefined ? true : acceptStringNumbers;\r\n\r\n var typeStr = typeof(obj);\r\n if ( typeStr === 'string' && acceptStringNumbers ) {\r\n obj = Number(obj);\r\n typeStr = typeof(obj);\r\n } \r\n if ( typeStr === 'bigint' ) {\r\n return 'bigint';\r\n }\r\n if ( typeStr !== 'number' ) {\r\n return ( obj && (Object.getPrototypeOf(obj) === Number.prototype)) ? 'numberObject' : 'NaN';\r\n }\r\n if ( isNaN(obj) ) {\r\n return 'NaN';\r\n } \r\n if ( !Number.isFinite(obj) ) {\r\n return ( obj < 0 ) ? '-infinity' : 'infinity'; \r\n }\r\n if ( Number.isSafeInteger(obj) ) {\r\n return 'safeInteger';\r\n } else {\r\n return ( Number.isSafeInteger( Number(obj.toFixed()) ) )? 'safeFloat' : 'unsafeNumber';\r\n }\r\n}\r\n/**\r\n * Tests to see if the number passed in is safe to use in a calculation. This is useful because Javascript\r\n * has a number of different types of numbers and some of them are not safe to use in calculations. E.g.\r\n * BigInts are not safe to use in calculations with regular numbers. Also, numbers that are too large to be\r\n * safe integers are not safe to use in calculations. \r\n * \r\n * Note also \r\n * 1. NaN returns false as it is not a safe number to use in calculations. \r\n * 2. Booleans also returns false because even though javascript coerces them into 0 or 1, relying on this\r\n * behaviour is not good practice and can lead to bugs.\r\n * 3. Number Objects (created via new Number(xxx)) return false. Some JavaScript methods\r\n * and functions behave differently when they receive an object instead of a primitive value.\r\n * E.g. when comparing with ===, new Number(10) !== 10.\r\n * \r\n * @param {*} obj - The object to check if it is a safe number.\r\n * \r\n * @param {*} acceptStringNumbers - Optional. if true (the default value), then if a string is passed in, it\r\n * will be converted to a number and it's type will tested for safe use. Strings\r\n * are not coerceced to numbers if they do not represent a valid number. E.g '34.345abchs'\r\n * will not be converted to a number and will return false. But '34.345' will be converted\r\n * to a number and will return true. String representing Hex numbers also work - E.g. 0xFF. \r\n * (Note - the built in javascript parseFloat() function can be used before calling this \r\n * function to force coercing. E.g. it will convert '34.345abchs' to 34.345).\r\n * if acceptStringNumbers is false then when a string is passed in, it will never be \r\n * converted to a number and false will be returned\r\n * \r\n * @returns - true if the number passed in is safe to use in a calculation, false otherwise.\r\n */\r\nfunction isSafeNum (obj, acceptStringNumbers) {\r\n acceptStringNumbers = (acceptStringNumbers === undefined) ? true : acceptStringNumbers;\r\n \r\n var typeStr = getNumTypeOf(obj, acceptStringNumbers);\r\n\r\n return (typeStr === 'safeInteger' || typeStr === 'safeFloat') ? true : false;\r\n}\r\n\r\nfunction isSafeNumber() {\r\n console.warn('Warning: isSafeNumber is deprecated. Please use isSafeNum.');\r\n return isSafeNum.apply(this, arguments);\r\n}\r\n\r\nfunction typeOfNumber() {\r\n console.warn('Warning: typeOfNumber is deprecated. Please use getNumTypeOf.');\r\n return getNumTypeOf.apply(this, arguments);\r\n}\r\n\r\nexport { getNumTypeOf };\r\nexport { isSafeNum };\r\n\r\nexport { isSafeNumber };\r\nexport { typeOfNumber };\r\n","/*******************************************************************************************************\r\n * Type-Analyser\r\n * MIT License\r\n * For full license details see the LICENSE file in the repo root or https://opensource.org/licenses/MIT\r\n * Copyright(c) 2023 Owen Cullum <dev@metagu.com>\r\n *******************************************************************************************************/\r\n\r\nimport { getTypeOf } from \"./getTypeOf.js\";\r\n/**\r\n * Checks if an object is JSON serializable. This is a recursive function that will check all properties of an object.\r\n * \r\n * @param {*} obj - the object to test\r\n * \r\n * @param {*} acceptFormatLoss - Optional. if false (the default), only return true for types that can be serializaed without problems.\r\n * \r\n * If this parmeter is true then this function also returns true for types where no data is lost but \r\n * the format is changed and can be easily converted back when de-serializing. E.g. 'Date', 'URL', \r\n * 'URLsearchparams' are converted to strings when serializing to JSON, so New Date( stringValue ) or\r\n * new URL( stringValue ) etc can be used to convert back.\r\n * \r\n * Typed arrays are converted to regular arrays when serializing to JSON so iterating over the results \r\n * of the parsed JSON element, adding to an array and then new TypedArray( array ) can be used to convert back. \r\n * \r\n * @param {*} visitedObjects - Used internally to detect circular references. Do not pass this parameter.\r\n * \r\n * @returns true if the object is JSON serializable WITHOUT a loss of data, false otherwise. Note that if 'acceptFormatLoss' is set \r\n * then this function returns true if during JSON serialization there's no actual data loss but the format may be changed. \r\n */\r\nfunction isJSONSerializable(obj, acceptFormatLoss, visitedObjects) {\r\n\r\n acceptFormatLoss = acceptFormatLoss === undefined ? false : acceptFormatLoss;\r\n visitedObjects = visitedObjects === undefined ? new Set() : visitedObjects;\r\n\r\n var validJSONTypes = ['string', 'number', 'boolean', 'undefined', 'null'];\r\n \r\n // types where no data is lost but there is a change in data format when serializing\r\n var lossyValidJSONTypes = [\r\n 'Date', 'URL', 'URLSearchParams', \r\n 'Int8Array', 'Uint8Array', 'Uint8ClampedArray', \r\n 'Int16Array', 'Uint16Array', \r\n 'Int32Array', 'Uint32Array', 'Float32Array',\r\n 'Float64Array', 'BigInt64Array', 'BigUint64Array'\r\n ];\r\n if (acceptFormatLoss) {\r\n validJSONTypes.push(...lossyValidJSONTypes);\r\n }\r\n\r\n var type = getTypeOf(obj);\r\n\r\n if (validJSONTypes.includes( type )) return true;\r\n\r\n if (type === 'object' || type === 'Array') {\r\n // check for circular references \r\n if (visitedObjects.has(obj)) { \r\n return false;\r\n }\r\n visitedObjects.add(obj);\r\n \r\n // Non integer array properties cannot be JSON serialized as data will be lost when serializing to JSON\r\n if (type === 'Array') { \r\n var totalPropertyCount = 0;\r\n for (var key in obj) {\r\n totalPropertyCount++;\r\n }\r\n if (totalPropertyCount > obj.length) return false;\r\n } \r\n\r\n // recursively check all properties \r\n for (var key in obj) {\r\n if (Object.prototype.hasOwnProperty.call(obj, key)) {\r\n if (!isJSONSerializable(obj[key], acceptFormatLoss, visitedObjects)) return false;\r\n }\r\n }\r\n return true;\r\n }\r\n\r\n // For functions, Symbols and other non-JSON data types NOT in validJSONTypes\r\n return false;\r\n}\r\n\r\n/**\r\n * Checks if an object has a circular reference. This is a recursive function that will check all properties of an object.\r\n * It works for ALL types of objects including custom and ES6 classes and is particularily useful for debugging. \r\n * \r\n * However, note:\r\n * 1. It checks object properties (i.e., their state) and does not check for circular references in methods or object prototypes \r\n * 2. It won't catch circular references in dynmically created properties (i.e., created when methods are called)\r\n * 3. If a custom or ES6 class overrides the default behavior of for...in or Object.keys, there may be problems \r\n * \r\n * @param {*} obj - the object to test\r\n * \r\n * @param {*} visitedObjects - Used internally to detect circular references. Do not pass this parameter.\r\n * \r\n * @returns true if the object has a circular reference, false otherwise.\r\n */\r\nfunction hasCircularRef(obj, visitedObjects) {\r\n\r\n visitedObjects = visitedObjects === undefined ? new WeakSet() : visitedObjects;\r\n\r\n if (typeof obj !== 'object' || obj === null) {\r\n return false;\r\n }\r\n\r\n if (visitedObjects.has(obj)) {\r\n return true;\r\n }\r\n\r\n visitedObjects.add(obj);\r\n\r\n for (var key in obj) {\r\n if (hasCircularRef(obj[key], visitedObjects)) {\r\n return true;\r\n }\r\n }\r\n\r\n return false;\r\n}\r\n\r\nfunction hasCircularReference() {\r\n console.warn('Warning: hasCircularReference is deprecated. Please use hasCircularRef.');\r\n return hasCircularRef.apply(this, arguments);\r\n}\r\n\r\nexport { isJSONSerializable };\r\nexport { hasCircularRef };\r\nexport { hasCircularReference };"],"names":["getTypeOf","obj","typeStr","basicType","hasOwnProperty","valueOf","toString","slice","Object","prototype","call","toLowerCase","constructor","es6ClassName","name","getNumTypeOf","acceptStringNumbers","undefined","Number","getPrototypeOf","isNaN","isFinite","isSafeInteger","toFixed","isSafeNum","hasCircularRef","visitedObjects","WeakSet","has","key","add","console","warn","apply","this","arguments","showFullPrototypeChain","resultInfo","Type","ReferenceVariable","hasCustomConstructor","prototypeChainString","prototypeChain","includes","pChain","proto","chain","push","getPrototypeChain","join","pop","length","isJSONSerializable","acceptFormatLoss","Set","validJSONTypes","type","totalPropertyCount"],"mappings":"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"}