testing-library / jest-dom

:owl: Custom jest matchers to test the state of the DOM
https://testing-library.com/docs/ecosystem-jest-dom
MIT License
4.45k stars 401 forks source link

Missing type definitions for expect imported from @jest/globals #426

Open arty-name opened 2 years ago

arty-name commented 2 years ago

Relevant code or config:

import { expect } from '@jest/globals';
import '@testing-library/jest-dom';
expect(container).toHaveAttribute('hidden');

What you did:

I imported jest globals from '@jest/globals'

What happened:

I got the TypeScript error message: TS2339: Property 'toHaveAttribute' does not exist on type 'Matchers '.

Reproduction:

import { expect } from '@jest/globals';
import '@testing-library/jest-dom';
expect(container).toHaveAttribute('hidden');

Problem description:

The custom matchers provided by jest-dom cannot be used in TypeScript if the expect is imported from @jest/globals. Only the global expect is modified, apparently.

Suggested solution:

Extend the interface of expect in the @jest/globals as well.

gnapse commented 2 years ago

Thanks for reporting this.

I have to say, I see this more as a problem with jest than with jest-dom, if really the solution involves duplicating the code that extends the interface of expect's return type. I hope there would a better solution where you extend it once, and no matter where you get expect from, it should work.

arty-name commented 2 years ago

After a deeper look, I see that the types of jest-dom extend jest.Matchers

Meanwhile, the types of @jest/globals simply re-export the type of the expect package

I’d naively expect that it’s rather expect.Matchers that should be extended, but after some poking around, I feel that I am out of my depth here.

IanVS commented 2 years ago

I'm importing expect directly from the expect package, and also struggling to get types working, since the way that jest-dom extends the types is by modifying the jest module, it seems. But I don't have that, since I'm using expect outside of jest. :(

muratx10 commented 2 years ago

In my case, I've just switched to expect that comes from jest, not from jest/globals and it works like a charm.

JoSuzuki commented 2 years ago

I wasn't able to solve it the proper way either, but I've made a patch on the types coming from @jest/globals, in a declarations.d.ts file at the root of my project I've added the following configuration:

import type { TestingLibraryMatchers } from '@types/testing-library__jest-dom/matchers';
// import type { expect } from '@jest/globals'; <-- this was necessary because of my specific setup of cypress + jest, otherwise it was inferring the wrong expect

declare module '@jest/globals/node_modules/expect/build/types' {
  // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/ban-types
  export interface Matchers<R = void, T = {}>
    extends TestingLibraryMatchers<typeof expect.stringContaining, R> {}
}

this feels a bit risky because it relies on a path that could change at any moment (@jest/globals/node_modules/expect/build/types), but it was the best solution I could find for now.

rtruong commented 2 years ago

Is there a solution for this yet?

aalpgiray commented 2 years ago

I assumed this should worked but couldn't get it to work yet.

import matchers from '@testing-library/jest-dom/matchers';
import { expect } from '@jest/globals';

expect.extend(matchers);
arty-name commented 2 years ago

Looks like Jest@28 should enable that

yordis commented 2 years ago

I added the following to my globals.d.ts file


import type { TestingLibraryMatchers } from '@types/testing-library__jest-dom/matchers';

declare module '@jest/expect' {
  export interface Matchers<R = void, T = {}> extends TestingLibraryMatchers<typeof expect.stringContaining, R> {}
}

@jest/globals exports from @jest/expect the expect function

import type { JestExpect } from '@jest/expect';
export declare const expect: JestExpect;
mrazauskas commented 2 years ago

Just to add: since Jest v28 the above should work equally with declare module 'expect' or declare module '@jest/expect'.

By the way, it might be a good idea to include this augmentation declaration in @types/testing-library__jest-dom. So that matchers would have correct types for those who import { expect } form '@jest/globals'.

Vinnl commented 1 year ago

I submitted a PR to DefinitelyTyped that I think should fix this issue: https://github.com/DefinitelyTyped/DefinitelyTyped/pull/65981

MattyBalaam commented 1 year ago

Wow, thanks @Vinnl I hit this problem yesterday and your fix has got rid of my issue :)

gnapse commented 1 year ago

I'm glad that this has fixed the issue for some. Unfortunately, it seems to be a source of problem for others. See https://github.com/DefinitelyTyped/DefinitelyTyped/discussions/65987#discussioncomment-6371630

And we already have a pull request proposing to revert the changes: https://github.com/DefinitelyTyped/DefinitelyTyped/pull/65990

I'm not clear what to do here. Would appreciate some help.

Vinnl commented 1 year ago

@gnapse Hmm, I'm also not sure. I don't know exactly how to reproduce, but looking at the error message, it looks like the problem might be that we're setting T = {}, whereas in expect, it's unknown:

https://github.com/jestjs/jest/blob/1f019afdcdfc54a6664908bb45f343db4e3d0848/packages/expect/src/types.ts#L139

However, if I try to change it in the @testing-library/jest-dom type definitions for the jest namespace, I get the same error message - because @types/jest does define it to be {}:

https://github.com/DefinitelyTyped/DefinitelyTyped/blob/e42746838dabadb4aa0542017f178a3c118f4e18/types/jest/index.d.ts#L844

I've submitted a followup PR that only changes it for expect and hope that that fixes it, but I can't verify because I don't know how to reproduce, and I'm not 100% confident about what I'm doing :/

abhi-works commented 1 year ago

I'm glad that this has fixed the issue for some. Unfortunately, it seems to be a source of problem for others. See DefinitelyTyped/DefinitelyTyped#65987 (comment)

And we already have a pull request proposing to revert the changes: DefinitelyTyped/DefinitelyTyped#65990

I'm not clear what to do here. Would appreciate some help.

@Vinnl @gnapse We face this issue while generating dist folder with script like "build:esm":"tsc" in package.json node_modules/@types/testing-library__jest-dom/index.d.ts:21:15 - error TS2428: All declarations of 'Matchers' must have identical type parameters. node_modules/expect/build/index.d.ts:107:26 - error TS2428: All declarations of 'Matchers' must have identical type parameters.

https://github.com/DefinitelyTyped/DefinitelyTyped/pull/65991 Can this reversal PR be temporarily approved to prevent any impact on the systems?

Vinnl commented 1 year ago

@abhi-works Is your code public somewhere that I could try it out? Or alternatively, could you open node_modules/@types/testing-library__jest-dom/index.d.ts and apply these changes (i.e. replace the first {} by unknown on line 21) manually, and report back whether that fixes the issue for you?

abhi-works commented 1 year ago

@Vinnl This issue occurs specifically when utilizing the tsc (TypeScript compiler) to generate a compiled distribution bundle. Code is not public,Will try manually in my local

abhi-works commented 1 year ago

Alternatively, you can replicate the issue by creating a simple React TypeScript component and its corresponding test case that makes use of @testing-library/jest-dom. Afterwards, you can generate the build using tsc to observe the problem @Vinnl

mrazauskas commented 1 year ago

@abhi-works Could you tell Jest and TS version you are using?

If I remember it right, the type arguments if the Matchers interface differ between versions of Jest. That might be tricky.

abhi-works commented 1 year ago

@abhi-works Is your code public somewhere that I could try it out? Or alternatively, could you open node_modules/@types/testing-library__jest-dom/index.d.ts and apply these changes (i.e. replace the first {} by unknown on line 21) manually, and report back whether that fixes the issue for you?

@Vinnl This issue occurs specifically when utilizing the tsc (TypeScript compiler) to generate a compiled distribution bundle. Code is not public,Will try manually in my local

@Vinnl @gnapse https://github.com/DefinitelyTyped/DefinitelyTyped/pull/65991/files Works locally

DevinXian commented 7 months ago

I added the following to my globals.d.ts file

import type { TestingLibraryMatchers } from '@types/testing-library__jest-dom/matchers';

declare module '@jest/expect' {
  export interface Matchers<R = void, T = {}> extends TestingLibraryMatchers<typeof expect.stringContaining, R> {}
}

@jest/globals exports from @jest/expect the expect function

import type { JestExpect } from '@jest/expect';
export declare const expect: JestExpect;

we'd better to install @jest/expect explicitly

brunomacf commented 5 months ago

For me I had to install @jest/expect alongside with @jest/globals in order to make import "@testing-library/jest-dom/jest-globals"; to correctly extend the expect type.