Question

How can TypeScript discriminate between an object and an array?

Answer and Explanation

In TypeScript, discriminating between an object and an array can be achieved through several methods. Here's a breakdown of common approaches:

1. Using `Array.isArray()`:

The simplest and most reliable way is to use the built-in `Array.isArray()` method. This function returns `true` if a value is an array and `false` otherwise. This works in Javascript as well as TypeScript:

function identifyType(value: any): string {
  if (Array.isArray(value)) {
    return "Array";
  } else if (typeof value === 'object' && value !== null) {
    return "Object";
  } else {
    return "Other";
  }
}

console.log(identifyType([1, 2, 3])); // Output: Array
console.log(identifyType({ a: 1, b: 2 })); // Output: Object
console.log(identifyType(null)); // Output: Other

2. Using `typeof` Operator:

The `typeof` operator can help distinguish between primitive types and objects. However, it returns `"object"` for both arrays and null values, so you need to be careful:

function checkType(data: any): string {
  if (typeof data === 'object' && data !== null) {
    if (Array.isArray(data)) {
      return "Array";
    } else {
      return "Object";
    }
  } else {
    return typeof data;
  }
}

console.log(checkType([1, 2, 3])); // Output: Array
console.log(checkType({ a: 1, b: 2 })); // Output: Object
console.log(checkType("hello")); // Output: string

3. Custom Type Guards:

You can define custom type guard functions to provide more specific type checking. This is especially useful when dealing with complex types:

function isArray(value: any): value is any[] {
  return Array.isArray(value);
}

function isObject(value: any): value is object {
  return typeof value === 'object' && value !== null && !Array.isArray(value);
}

function processValue(value: any) {
  if (isArray(value)) {
    console.log("This is an Array:", value);
  } else if (isObject(value)) {
    console.log("This is an Object:", value);
  } else {
    console.log("This is neither an Array nor an Object:", value);
  }
}

processValue([1, 2, 3]); // Output: This is an Array: [1, 2, 3]
processValue({ a: 1, b: 2 }); // Output: This is an Object: { a: 1, b: 2 }
processValue("hello"); // Output: This is neither an Array nor an Object: hello

4. Duck Typing (Structural Typing):

TypeScript uses structural typing, also known as "Duck Typing". You can check for specific properties or methods to infer the type. While not directly discriminating between objects and arrays, it's useful for narrowing down types based on their structure:

interface HasLength {
  length: number;
}

function process(item: any) {
  if (typeof item === 'object' && item !== null && 'length' in item) {
    console.log("This has a length property:", item.length);
  } else {
    console.log("This does not have a length property");
  }
}

process([1, 2, 3]); // Output: This has a length property: 3
process({ length: 5 }); // Output: This has a length property: 5
process({ a: 1, b: 2 }); // Output: This does not have a length property

By combining these techniques, you can effectively discriminate between objects and arrays in TypeScript and write type-safe code.

More questions