Custom Jest Matcher: toBeInstanceOf
Jest's built-in matchers are powerful, but sometimes you need more specific checks. This challenge asks you to create a custom Jest matcher called toBeInstanceOf that verifies if a value is an instance of a specified class or constructor function. This is particularly useful when working with inheritance and ensuring objects are of the expected type.
Problem Description
You need to implement a custom Jest matcher named toBeInstanceOf. This matcher should take a constructor function (class or function) as an argument and return true if the value being tested is an instance of that constructor, and false otherwise. The matcher should work correctly with both classes and regular functions used as constructors.
Key Requirements:
- The matcher must be named
toBeInstanceOf. - It must accept a single argument: the constructor function (class or function).
- It must use the
instanceofoperator to perform the type check. - It should provide a clear and informative failure message if the check fails.
- It should handle cases where the constructor is
nullorundefinedgracefully (returningfalseand providing a helpful message). - It should handle primitive values correctly (returning
false).
Expected Behavior:
value.toBeInstanceOf(Constructor)should returntrueifvalueis an instance ofConstructor.value.toBeInstanceOf(Constructor)should returnfalseifvalueis not an instance ofConstructor.- The failure message should clearly indicate the expected type (the constructor) and the actual type of the value.
Edge Cases to Consider:
nullorundefinedconstructor: Should returnfalseand provide a descriptive error message.- Primitive values (string, number, boolean, symbol, etc.): Should return
false. - Classes and functions used as constructors.
- Inheritance: An instance of a subclass should also pass the
toBeInstanceOfcheck for its parent class.
Examples
Example 1:
class Animal {}
class Dog extends Animal {}
const myDog = new Dog();
expect(myDog).toBeInstanceOf(Dog); // true
expect(myDog).toBeInstanceOf(Animal); // true (due to inheritance)
expect(myDog).toBeInstanceOf(Object); // false
expect("hello").toBeInstanceOf(Dog); // false
Explanation: myDog is an instance of Dog and Animal, so the matcher returns true in both cases. A string is not an instance of Dog, so the matcher returns false.
Example 2:
function MyFunc() {}
const myFuncInstance = new MyFunc();
expect(myFuncInstance).toBeInstanceOf(MyFunc); // true
expect(myFuncInstance).toBeInstanceOf(Function); // false
Explanation: myFuncInstance is an instance of MyFunc, but not of Function itself.
Example 3: (Edge Case)
class MyClass {}
expect(null).toBeInstanceOf(MyClass); // Error: Expected null not to be an instance of MyClass.
expect(undefined).toBeInstanceOf(MyClass); // Error: Expected undefined not to be an instance of MyClass.
Explanation: Passing null or undefined as the constructor should result in a clear error message.
Constraints
- The matcher must be implemented in TypeScript.
- The matcher should be compatible with Jest versions 25 or higher.
- The matcher should not introduce any external dependencies.
- The matcher should be performant; the
instanceofoperator is generally efficient.
Notes
- Remember that Jest matchers are expected to have
passandmessagemethods. - Consider how to provide a helpful error message when the check fails, including the expected and actual types.
- Think about how to handle edge cases gracefully to prevent unexpected errors.
- The
instanceofoperator can be tricky with cross-frame or cross-realm scenarios, but for this challenge, you can assume a standard JavaScript environment.