Hone logo
Hone
Problems

Implement toStrictEqual Matcher for Jest

Jest's toStrictEqual matcher is a powerful tool for asserting the deep equality of objects, arrays, and primitives, with a crucial distinction: it also checks that the types of the properties and the structure of the objects are identical. This challenge asks you to implement a simplified version of this matcher in TypeScript.

Problem Description

Your task is to create a custom Jest matcher function named toStrictEqual. This matcher should compare two values for strict equality.

Key Requirements:

  1. Deep Equality Check: The matcher must recursively compare the contents of objects and arrays.
  2. Type Strictness: Unlike a simple deep equality check, toStrictEqual should fail if the types of corresponding properties within objects or elements within arrays do not match, even if their values are equivalent.
  3. Primitive Comparison: For primitive types (numbers, strings, booleans, null, undefined, symbols, bigints), it should behave like ===.
  4. Handling of Special Objects: Consider how to handle built-in object types like Date, RegExp, and Map/Set if you choose to implement them (though focusing on plain objects and arrays is sufficient for a core implementation).
  5. Error Reporting: When the assertion fails, provide a clear and informative message indicating what mismatched.

Expected Behavior:

  • toStrictEqual(expected, actual) should return true if actual is strictly deeply equal to expected.
  • It should return false otherwise, triggering a Jest assertion failure with a descriptive message.

Edge Cases:

  • Comparing null and undefined.
  • Comparing different primitive types (e.g., 1 and "1").
  • Comparing objects with different numbers of properties.
  • Comparing arrays of different lengths.
  • Circular references (optional, but good to be aware of).

Examples

Example 1:

// Expected to pass
expect({ a: 1, b: "hello" }).toStrictEqual({ a: 1, b: "hello" });
expect([1, { x: 5 }]).toStrictEqual([1, { x: 5 }]);
expect(1).toStrictEqual(1);
expect(null).toStrictEqual(null);

Example 2:

// Expected to fail (type mismatch for primitive)
expect(1).toStrictEqual("1");

// Expected to fail (type mismatch for property)
expect({ a: 1 }).toStrictEqual({ a: "1" });

// Expected to fail (different number of properties)
expect({ a: 1, b: 2 }).toStrictEqual({ a: 1 });

// Expected to fail (different array elements)
expect([1, 2]).toStrictEqual([1, 3]);

Example 3:

// Expected to fail (different object structures)
expect([{ a: 1 }]).toStrictEqual({ 0: { a: 1 } });

// Expected to fail (primitive vs object)
expect(1).toStrictEqual({ value: 1 });

Constraints

  • The implementation should be written in TypeScript.
  • Assume you are integrating this into Jest's custom matcher system. You'll need to return an object with a pass boolean and a message string.
  • Focus on plain JavaScript objects and arrays for the core implementation.

Notes

  • Consider using a recursive approach for comparing nested structures.
  • Think about how to differentiate between different data types (e.g., Array.isArray, typeof, Object.prototype.toString.call).
  • The message function in Jest's custom matchers is responsible for generating the failure output. You'll need to construct a clear message that highlights the differences found.
  • For a more advanced implementation, you might consider how to handle instances of custom classes or built-in types like Date or RegExp.
Loading editor...
typescript