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:
- Deep Equality Check: The matcher must recursively compare the contents of objects and arrays.
- Type Strictness: Unlike a simple deep equality check,
toStrictEqualshould fail if the types of corresponding properties within objects or elements within arrays do not match, even if their values are equivalent. - Primitive Comparison: For primitive types (numbers, strings, booleans, null, undefined, symbols, bigints), it should behave like
===. - Handling of Special Objects: Consider how to handle built-in object types like
Date,RegExp, andMap/Setif you choose to implement them (though focusing on plain objects and arrays is sufficient for a core implementation). - Error Reporting: When the assertion fails, provide a clear and informative message indicating what mismatched.
Expected Behavior:
toStrictEqual(expected, actual)should returntrueifactualis strictly deeply equal toexpected.- It should return
falseotherwise, triggering a Jest assertion failure with a descriptive message.
Edge Cases:
- Comparing
nullandundefined. - Comparing different primitive types (e.g.,
1and"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
passboolean and amessagestring. - 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
messagefunction 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
DateorRegExp.