Implement Type Utilities II
The sequel to `Type Utilities` moves from primitives to the non-primitive types, where `typeof` stops being enough on its own. Arrays, functions, and plain objects are all `typeof 'object'` or close to it, so telling them apart means reaching one level deeper — into the prototype chain.
The problem
Implement `isArray`, `isFunction`, `isObject`, and `isPlainObject`. `isObject` should be true for arrays, functions, and plain objects alike (anything that isn't `null`, `undefined`, or a primitive), while `isPlainObject` should be true only for a POJO — an object literal, or one made with `Object.create(null)` — and false for arrays, functions, class instances, or built-ins like `Date`.
The trap is that `typeof` can't distinguish a plain object from an array, a `Date`, or a class instance — all four report `typeof 'object'`. Telling them apart requires comparing the object's actual prototype, not just its `typeof`.
The approach
`isArray` and `isFunction` are the easy half: `Array.isArray` already exists precisely because `typeof` can't tell an array from an object, and `typeof value === 'function'` is one of the few `typeof` results that's unambiguous on its own.
`isObject` broadens `typeof` to two branches — `typeof value === 'object'` covers plain objects, arrays, and `null`, and `typeof value === 'function'` covers functions (which `typeof` treats as its own category, not `'object'`). Excluding `null` explicitly with `value !== null` is what keeps `isObject(null)` false, since `typeof null` would otherwise sneak through the first branch.
`isPlainObject` is the one that needs the prototype chain: after confirming `isObject(value)`, compare `Object.getPrototypeOf(value)` against `Object.prototype`. An array's prototype is `Array.prototype`, a function's is `Function.prototype`, a `Date`'s is `Date.prototype` — none of them equal `Object.prototype`, so they're all correctly rejected without needing their own special-case checks. The one explicit addition is allowing a `null` prototype, since `Object.create(null)` produces an object with no prototype at all, which the spec still counts as plain.
The solution
/**
* @param {unknown} value
* @returns {boolean}
*/
export function isArray(value) {
return Array.isArray(value);
}
/**
* @param {unknown} value
* @returns {boolean}
*/
export function isFunction(value) {
return typeof value === "function";
}
/**
* @param {unknown} value
* @returns {boolean}
*/
export function isObject(value) {
return value !== null && (typeof value === "object" || typeof value === "function");
}
/**
* @param {unknown} value
* @returns {boolean}
*/
export function isPlainObject(value) {
if (!isObject(value)) return false;
const proto = Object.getPrototypeOf(value);
return proto === null || proto === Object.prototype;
}Time O(1)Space O(1)