All files getType.js

94.11% Statements 16/17
95% Branches 19/20
100% Functions 1/1
94.11% Lines 16/17

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104                                                                                                                  35x 706x 17x       689x 17x         672x 38x         634x       2x       632x 1x         631x 34x 34x 34x             597x        
/**
 * Type detection beyond `typeof`
 * 
 * Copyright (C) 2022  Dieter Raber
 * https://opensource.org/licenses/MIT
 */
/**
 * More consistent variety of `typeof`. It returns a string such as `Null`, `Undefined`, `Function`, `String`, etc.
 * These values are case sensitive and mostly correspond to the constructor names.
 * @param {*} value The value to check
 * @returns {String}
 * @example
 * // getType() is not limited to the examples below
 * const v1 = null;
 * getType(v1); // "Null"
 * 
 * let v2;
 * getType(v2); // "Undefined"
 * 
 * const v3 = function() {};
 * getType(v3); // "Function", use isCallable() to cover async functions and generators as well
 * 
 * const v4 = async function() {};
 * getType(v4); // "AsyncFunction"
 * 
 * const v5 = function* generator(i) {};
 * getType(v5); // "GeneratorFunction"
 * 
 * const v6 = { foo: "bar" };
 * getType(v6); // "PlainObject", not "Object", as you may have expected
 * 
 * const v7 = new Map();
 * getType(v7); // "Map"
 * 
 * const v8 = new Set();
 * getType(v8); // "Set"
 * 
 * const v9 = new WeakMap();
 * getType(v9); // "WeakMap"
 * 
 * const v10 = new WeakSet();
 * getType(v10); // "WeakSet"
 * 
 * const v11 = new Date();
 * getType(v11); // "Date"
 * 
 * const v12 = new Error();
 * getType(v12); // "Error"
 * 
 * const v13 = new Promise(() => {});
 * getType(v13); // "Promise"
 * 
 * class CustomClass {}
 * const v14 = new CustomClass();
 * getType(v14); // "CustomClass"
 * getType(class CustomClass {}) // "Class", note the difference: CustomClass has not been initiated yet
 */
const getType = (value) => {
    if (typeof value === "undefined") {
        return "Undefined";
    }
 
    // `null` is an object, but has no constructor
    if (value === null) {
        return "Null";
    }
 
    // `Object` in this context is ambiguous, `PlainObject` on the other hand
    // is pretty established in the industry and used by other libraries as well.
    if (value.constructor.name === "Object") {
        return "PlainObject";
    }
    
    // ES6 classes that aren't initiated with `new` would be reported with `Function`
    // instead of `Class`. Parsing the input value as string can fix this
    if (
        value.constructor.name === "Function" &&
        /^class\s+([\w]+\s+)?{/.test(value.toString().replace(/\s+/gs, " "))
    ) {
        return "Class";
    }
 
    // Chrome at this point reports elements of the type `MathMLElement` wrongly as `Element`
    if (value.constructor.name === "Element" && value.namespaceURI.endsWith('MathML')) {
        return "MathMLElement";
    }
 
    // Work around objects with the own property `name` as a method
    // @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/name#sect3
    if (typeof value.constructor.name === "function") {
        const match = value.constructor.toString().match(/[^\s]+\s+([\w]+)/);
        Eif (match && match.length > 0) {
            return match[1];
        }
        return "Object";
    }
 
    // The name of the constructor, for example `Array`, `Object`,
    // `Number`, `String`, `Boolean` or `MyCustomObject`
    return value.constructor.name;
};
 
export default getType;