Predicate returning negative type assertion

I need help writing two Jest functions that can verify if an object is an instance of a specific type or not.

The function expectInstanceOf works perfectly, but unfortunately, the function expectNotInstanceOf is not functioning as expected.

export function expectInstanceOf<E, A extends unknown[]>(obj: unknown, type: new (...args: A) => E): asserts obj is E {
  expect(obj).toBeInstanceOf(type);
}

export function expectNotInstanceOf<E, A extends unknown[]>(obj: unknown, type: new (...args: A) => E): asserts obj is Exclude<typeof obj, E> {
  expect(obj).not.toBeInstanceOf(type);
}

class Foo {
  foo() {
    /**/
  }
}
class Bar {
  bar() {
    /**/
  }
}

function foo(obj: Foo | Bar) {
  expectInstanceOf(obj, Foo);
  obj.foo();
}

function notFoo(obj: Foo | Bar) {
  expectNotInstanceOf(obj, Foo);
  obj.bar(); // Property 'bar' does not exist on type 'Foo | Bar'.
}

Is there a way to correct the functionality of expectNotInstanceOf?

Answer №1

When looking at your code, the typeof obj will always result in being of the unknown type. This means you cannot use the typeof type query operator to "abstract" a specific type. Additionally, the Exclude<T, U> utility type only filters union types within T. Since unknown is not a union type, using Exclude<unknown, E> will likely return unknown (or possibly the never type if E is unknown). Therefore, while expectInstanceOf narrows to E, expectNotInstanceOfnarrows to unknown, which isn't useful.

To address this issue, consider giving obj its own generic type parameter (let's say, T) and adjust the narrowing based on T. Here's an example:

function expectInstanceOf<T, E, A extends unknown[]>(
    obj: T, type: new (...args: A) => E): asserts obj is T & E { }

function expectNotInstanceOf<T, E, A extends unknown[]>(
    obj: T, type: new (...args: A) => E): asserts obj is T & Exclude<T, E> { }

With this modification, the sample code now functions as desired:

function foo(obj: Foo | Bar) {
    expectInstanceOf(obj, Foo);
    obj.foo(); // works fine
}

function notFoo(obj: Foo | Bar) {
    expectNotInstanceOf(obj, Foo);
    obj.bar(); // also works without issues
}

Link to playground with code

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

The class instances are not invoking the decorators

I'm experiencing issues with my decorators. It seems that the decorators are not being invoked on every instance of the class. While I understand that decorators are called during declaration time, I am wondering if there is a way to call them for eac ...

What could be the reason behind the for loop not running within a typescript function?

My confusion lies in the for loop within this function that seems to never run. Each console log is set up to return a specific value, but the looping action doesn't trigger. Can someone provide insight into what might be causing this issue? export fu ...

Leveraging AngularJS services within an Angular service

I am currently in the process of transitioning my AngularJS application to Angular. To facilitate this transition, I plan to create a hybrid application that combines both frameworks until the conversion is complete. However, I have encountered an issue wi ...

Experiencing a bug in my express application: TypeError - Unable to access properties of undefined (reading 'prototype')

I've encountered the TypeError: Cannot read properties of undefined (reading 'prototype') error in my javascript file. Despite researching online, it seems that many attribute this issue to importing {response} from express, but I am not doi ...

Functions outside of the render method cannot access the props or state using this.props or this.state

Just starting out with react. I've encountered an issue where a prop used in a function outside the render is coming up as undefined, and trying to use it to set a state value isn't working either. I've researched this problem and found va ...

How can I integrate a timer into an Angular carousel feature?

I have put together a carousel based on a tutorial I found on a website. Check out the HTML code for my carousel below: <div class="row carousel" (mouseover)="mouseCheck()"> <!-- For the prev control button ...

Utilize data binding in Typescript to easily link variables between a service and controller for seamless utilization

Is there a way to overcome the issue of value assignment not binding data in this scenario? For example, using this.arrayVal = someService.arrayVal does not work as intended. The objective is to simplify the assignment in both HTML and controller by using ...

Unexpected behavior: Promise.catch() fails to catch exception in AngularJS unit test

During the process of writing Jasmine unit tests for my Typescript app and running them via Resharper, I encountered an issue with executing an action when the handler throws an exception: describe("Q Service Test", () => { var q: ng.IQService; ...

Choosing the Right Language for AngularJS 2: TypeScript, JavaScript, or Dart?

AngularJS 2 is on the horizon, and the documentation recommends three languages: Typescript, Javascript, and Dart. As someone who primarily works with Javascript EcmaScript 5, I'm curious about the strengths and weaknesses of these three options. Cu ...

What is the process for performing type checking on a block of TypeScript code stored in memory?

I am currently integrating TypeScript support into my project called Data-Forge Notebook. My goal is to compile, perform type checking, and evaluate snippets of TypeScript code within the application. Compiling the code seems straightforward using the tr ...

Unable to build due to TypeScript Firebase typings not being compatible

This is what I have done: npm install firebase --save typings install npm~firebase --save After executing the above commands, my typings.json file now looks like this: { "ambientDevDependencies": { "angular-protractor": "registry:dt/angular-protract ...

Add the onclick() functionality to a personalized Angular 4 directive

I'm facing an issue with accessing the style of a button in my directive. I want to add a margin-left property to the button using an onclick() function in the directive. However, it doesn't seem to be working. Strangely, setting the CSS from the ...

Dealing with precompile array warning when utilizing a basic router in Angular 2

I am currently working on developing a straightforward router-based application in Angular2 using typescript. The version of Angular2 I am using is 2.0.0-rc.4, and the router version is 3.0.0-beta.1 Here is my Routes configuration- App.routes.ts import ...

Linking Redux to the highest level of my application is not functioning as expected

I have been attempting to troubleshoot this code for quite some time now, but I am struggling to identify the issue at hand. My main goal is to establish a connection between my top-level application and the redux store. However, every time I try, the stor ...

Unable to remove loading.tsx file

Currently tackling a project using Next.js, I decided to include loading.tsx within the app directory. However, upon attempting to delete it, an error crops up: Caused by: The system cannot find the file specified. (os error 2) The import trace for the r ...

How can the ChangeDetectorRef be leveraged to identify and respond to changes in component state for seamless integration with the material stepper component

While working with the Angular 8 Material Stepper, I am validating form states and setting stepCompleted to true when the conditions pass. You can view a demo of this functionality on Stackblitz: https://stackblitz.com/edit/angular-mat-stepper-demo-with-f ...

Exploring the fusion of different interfaces and props in React using typescript

I have designed an interface as shown below, representing the "base button". export interface ButtonProps { backgroundColor?: Colors, children?: React.ReactNode | JSX.Element, style?: CSSProperties, disabled?: boolean, onClick?: () => ...

Working with Typescript: Defining the return type of a function that extracts a subset of an object

Currently, I am attempting to create a function that will return a subset of an object's properties. However, I’m facing some issues with my code and I can't pinpoint the problem. const initialState = { count: 0, mounted: false, } type St ...

I am unable to invoke this function: TypeError: this.fetchData is not a function

Whenever I try to execute this.fetchData();, I encounter the error "TypeError: this.fetchData is not a function". Initially, I suspected that the context of 'this' was lost so I attempted using bind and arrow functions without success. How can I ...

How can I utilize Pinia and TypeScript to set the State using an Action?

I have a Pinia + TypeScript store named user.ts with the following structure: import { User } from 'firebase/auth'; import { defineStore } from 'pinia'; export const useUserStore = defineStore('user', { state: () => ( ...