Implementing Jest's toStrictEqual Matcher
Jest's toStrictEqual matcher is a crucial tool for asserting deep equality between objects, but with a key difference from toEqual. It checks for both structural equality (same keys and values) and strict equality of the values themselves (using ===). This challenge asks you to implement a simplified version of toStrictEqual to solidify your understanding of deep comparison and strict equality in JavaScript/TypeScript.
Problem Description
You are tasked with creating a function strictEqual that mimics the core functionality of Jest's toStrictEqual. This function should take two arguments, actual and expected, and return true if they are strictly equal according to the rules outlined below, and false otherwise. The goal is to understand how deep equality is checked while maintaining strict value comparisons.
Key Requirements:
- Recursive Comparison: The function must recursively compare objects and arrays.
- Strict Equality (
===): Values within objects and arrays must be compared using the strict equality operator (===). - Type Sensitivity: The comparison must be type-sensitive. For example,
1(number) should not be considered equal to"1"(string). - Handles Primitive Types: The function should correctly handle primitive types (numbers, strings, booleans, null, undefined, symbols).
- Handles Arrays: The function should compare arrays element by element.
- Handles Objects: The function should compare objects by iterating through their keys and comparing the values associated with those keys.
- Handles Circular References: The function should not handle circular references. Throwing an error is acceptable in this case.
Expected Behavior:
strictEqual(1, 1)should returntrue.strictEqual("hello", "hello")should returntrue.strictEqual(true, true)should returntrue.strictEqual(null, null)should returntrue.strictEqual(undefined, undefined)should returntrue.strictEqual(1, "1")should returnfalse.strictEqual([1, 2, 3], [1, 2, 3])should returntrue.strictEqual([1, 2, 3], [1, 2, "3"])should returnfalse.strictEqual({ a: 1, b: 2 }, { a: 1, b: 2 })should returntrue.strictEqual({ a: 1, b: 2 }, { a: 1, b: 3 })should returnfalse.strictEqual({ a: 1, b: { c: 2 } }, { a: 1, b: { c: 2 } })should returntrue.strictEqual({ a: 1, b: { c: 2 } }, { a: 1, b: { c: 3 } })should returnfalse.strictEqual({ a: 1, b: [1,2] }, { a: 1, b: [1,2] })should returntrue.strictEqual({ a: 1, b: [1,2] }, { a: 1, b: [1,3] })should returnfalse.
Edge Cases to Consider:
NaN:NaN === NaNis alwaysfalse. Your implementation should reflect this.- Circular references: Attempting to compare objects with circular references should result in an error (or at least not cause an infinite loop).
Examples
Example 1:
Input: strictEqual(5, 5)
Output: true
Explanation: Both values are the same primitive type (number) and have the same value.
Example 2:
Input: strictEqual({ a: 1, b: 2 }, { a: 1, b: "2" })
Output: false
Explanation: The values associated with key 'b' are not strictly equal (number vs. string).
Example 3:
Input: strictEqual([1, 2, 3], [1, 2, 3])
Output: true
Explanation: All elements in the arrays are strictly equal.
Constraints
- The function must be implemented in TypeScript.
- The function should not use any external libraries beyond the standard JavaScript/TypeScript library.
- The function should handle objects and arrays with a maximum depth of 10. Deeper nesting is not required.
- The function should not handle circular references.
Notes
- Consider using recursion to traverse objects and arrays.
- Pay close attention to the strict equality operator (
===). - Think about how to handle different data types correctly.
- Error handling for circular references is not strictly required, but a reasonable approach (e.g., throwing an error) is preferred over an infinite loop.
- The goal is to understand the logic behind deep strict equality, not to perfectly replicate Jest's
toStrictEqual(which has additional features and optimizations).