Implementing a Custom `typeof` Operator in JavaScript



Implementing a custom typeof operator in JavaScript involves creating a function that returns the type of a given value. While the built-in typeof operator handles most cases, it can be useful to create a custom version for learning purposes or to handle specific scenarios. In this episode, we'll create a custom typeof function that correctly identifies various data types, including primitives, objects, arrays, functions, and more.


What is the typeof Operator?

The typeof operator returns a string indicating the type of the unevaluated operand. It's commonly used to check the type of a variable in JavaScript.

Real Interview Insights

Interviewers might ask you to:

  • Implement a custom typeof function.
  • Handle various data types, including primitives, objects, arrays, functions, and special cases like null and NaN.

Implementing Custom typeof

Here's an implementation of a custom typeof function:

function customTypeof(value) {
  if (value === null) {
    return 'null';
  }
 
  if (Array.isArray(value)) {
    return 'array';
  }
 
  if (value instanceof Date) {
    return 'date';
  }
 
  if (value instanceof RegExp) {
    return 'regexp';
  }
 
  const type = typeof value;
 
  if (type === 'number' && isNaN(value)) {
    return 'nan';
  }
 
  if (type === 'object' && value !== null && typeof value[Symbol.iterator] === 'function') {
    return 'iterator';
  }
 
  return type;
}
Explanation:
  • Null Handling: Return 'null' for null values.
  • Array Handling: Return 'array' for arrays using Array.isArray.
  • Date Handling: Return 'date' for Date objects.
  • RegExp Handling: Return 'regexp' for RegExp objects.
  • NaN Handling: Return 'nan' for NaN values.
  • Iterator Handling: Return 'iterator' for iterable objects (e.g., generators, sets).
  • Default Handling: Use the built-in typeof for other cases.

Practical Examples

Consider examples with various data types:

console.log(customTypeof(null)); // Output: 'null'
console.log(customTypeof([])); // Output: 'array'
console.log(customTypeof(new Date())); // Output: 'date'
console.log(customTypeof(/regex/)); // Output: 'regexp'
console.log(customTypeof(NaN)); // Output: 'nan'
console.log(customTypeof((function* () {})())); // Output: 'iterator'
console.log(customTypeof(123)); // Output: 'number'
console.log(customTypeof('Hello')); // Output: 'string'
console.log(customTypeof(true)); // Output: 'boolean'
console.log(customTypeof(undefined)); // Output: 'undefined'
console.log(customTypeof(function () {})); // Output: 'function'
console.log(customTypeof({})); // Output: 'object'

Handling Edge Cases

  1. Complex Objects: Correctly identify custom objects and special cases.
  2. Iterables: Detect and return 'iterator' for iterable objects.
  3. Primitive Values: Ensure correct handling of primitive types.

Enhanced Implementation with More Types

function customTypeof(value) {
  if (value === null) {
    return 'null';
  }
 
  if (Array.isArray(value)) {
    return 'array';
  }
 
  if (value instanceof Date) {
    return 'date';
  }
 
  if (value instanceof RegExp) {
    return 'regexp';
  }
 
  if (typeof value === 'object' && value.toString() === '[object Set]') {
    return 'set';
  }
 
  if (typeof value === 'object' && value.toString() === '[object Map]') {
    return 'map';
  }
 
  if (typeof value === 'object' && value.toString() === '[object WeakSet]') {
    return 'weakset';
  }
 
  if (typeof value === 'object' && value.toString() === '[object WeakMap]') {
    return 'weakmap';
  }
 
  if (typeof value === 'symbol') {
    return 'symbol';
  }
 
  if (typeof value === 'bigint') {
    return 'bigint';
  }
 
  const type = typeof value;
 
  if (type === 'number' && isNaN(value)) {
    return 'nan';
  }
 
  if (type === 'object' && value !== null && typeof value[Symbol.iterator] === 'function') {
    return 'iterator';
  }
 
  return type;
}
 
// Example usage with additional types
console.log(customTypeof(new Set())); // Output: 'set'
console.log(customTypeof(new Map())); // Output: 'map'
console.log(customTypeof(new WeakSet())); // Output: 'weakset'
console.log(customTypeof(new WeakMap())); // Output: 'weakmap'
console.log(customTypeof(Symbol('symbol'))); // Output: 'symbol'
console.log(customTypeof(123n)); // Output: 'bigint'

Use Cases for Custom typeof

  1. Custom Type Checking: Tailoring the type checking process for specific needs.
  2. Debugging: Providing better control over how types are identified for debugging purposes.
  3. Learning: Understanding the intricacies of type checking in JavaScript.