Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Typescript error when importing jest-extended #367

Closed
iliubinskii opened this issue Oct 13, 2021 · 55 comments · Fixed by #407 or #559
Closed

Typescript error when importing jest-extended #367

iliubinskii opened this issue Oct 13, 2021 · 55 comments · Fixed by #407 or #559

Comments

@iliubinskii
Copy link
Contributor

Bug

jest-extended version: 1.0.0

.ts file:

import * as matchers from "jest-extended";

I get error:
File '.../node_modules/jest-extended/types/index.d.ts' is not a module.

Also, after upgrading to 1.0.0 unassigned import does not work any more:

import "jest-extended";

So:
Unassigned import stoped working in version 1.0.0
And wildcard import does not work in typescript because of invalid .d.ts file.

@linusnorton
Copy link

I'm also seeing this issue

@sevenseacat
Copy link

sevenseacat commented Oct 14, 2021

I've been using:

const matchers = require('jest-extended')
expect.extend(matchers);

To get 1.0.0 working in my TypeScript app. Nothing else seemed to work!

edit: nope that doesn't seem to work after adding some more tests... now I have this error everywhere

error TS2551: Property 'toReject' does not exist on type 'JestMatchers<Promise<void>>'. Did you mean 'rejects'?

But all the previously-passing tests using jest-extended still pass....

@abc-moviestarplanet
Copy link

I have the same issue too. My tests used to run fine when doing:

import "jest-extended"

But after updating to v1.0.0, every function I used gives me the error such as:

expect(...).toBeEmpty is not a function".

I tried the full typescript setup as documented (using global.d.ts, include), but did not help.

@silverwind
Copy link
Contributor

May be related to a2904bd which includes a breaking change when setupFilesAfterEnv is used. I had to add this /all suffix to get the matchers defined again.

@cduff
Copy link

cduff commented Oct 15, 2021

I was caught out by this too. I previous had this at the top of each file that used jest-extended:

import "jest-extended";

To get things working again with v1.0.0 I removed all these imports and now have:

In setupTests.ts (called by setupFilesAfterEnv config):

import "jest-extended/all";

In global.d.ts:

/// <reference types="jest-extended" />

Now everything is working again properly and it's simpler as jest-extended doesn't need to be imported all over the place.

@Ashlook
Copy link

Ashlook commented Oct 15, 2021

Had the same issue here when migrating to (ts-)jest 27 and jext-extended 1.0.0.

Before

I have in global.d.ts :

import 'jest-extended';

In jest.config.ts :

/* ... */
setupFilesAfterEnv: ['jest-extended'],
/* ... */

After

I just changed my jest.config.ts and add /all :

/* ... */
setupFilesAfterEnv: ['jest-extended/all'],
/* ... */

Everything seems to work fine with this change, like @silverwind pointed.

@revmischa
Copy link

I tried every option here and on the TS setup in the docs with v1.0.0 and I also get foo.test.ts:95:20 - error TS2339: Property 'toIncludeAllPartialMembers' does not exist on type 'JestMatchers...

@SimenB
Copy link
Member

SimenB commented Oct 18, 2021

Missing functions might just be misses in the type file (https://github.com/jest-community/jest-extended/blob/main/types/index.d.ts). Please send PRs for any missing! 🙂

We should have tests for the types so we're sure we don't miss any...

@idan-at
Copy link
Contributor

idan-at commented Oct 18, 2021

here is a fix for that specific missing type

@SimenB
Copy link
Member

SimenB commented Oct 18, 2021

@taylor-knapp
Copy link

taylor-knapp commented Oct 18, 2021

It seems to me to following commit is the culprit for this -- the default entry point for jest-extended no longer extends the jest matchers, which breaks existing configurations:

a2904bd

(i.e. anywhere jest-extended was imported / referenced in a jest config will no longer actually extend the matchers -- all references must be changed to jest-extended/all.)

I guess since it was mentioned off-handedly in the release notes, it was expected, though painful for consumers. 🤷

https://github.com/jest-community/jest-extended/releases/tag/v1.0.0

@SimenB
Copy link
Member

SimenB commented Oct 18, 2021

Yes, not extending (without /all) is on purpose. TypeScript errors are not though, so please send PRs for any errors there

@codejedi365
Copy link

codejedi365 commented Oct 24, 2021

I have tried everything here and on the README, and I'm not having typescript compilation errors but instead I am having TypeErrors during the JS execution while using ts-jest.

Specifically I'm receiving issues with toBeEmpty() and toBeArrayOfSize(), VSCode recognizes these as functions in the editor but when running the testcases the implementation cannot be found. I am using /all as stated above.

TypeError: expect(...).not.toBeEmpty is not a function

TypeError: expect(...).toBeArrayOfSize is not a function

@replete
Copy link

replete commented Oct 29, 2021

Screen Shot 2021-10-29 at 23 22 49

My workaround for this is as follows:

in setupTests.ts

// @ts-ignore
import matchers from 'jest-extended/all'
expect.extend(matchers)

Because typescript can't find definitions, it will complain about every use of the new jest-extended methods. To override this behaviour in the tsc compiler, we have to add a // @ts-ignore comment above each line of code implementing on of thises methods

In my tests that use a jest-extended matcher method:

// @ts-ignore
expect(res.body).toContainKey('users')
// @ts-ignore
expect(res.body.users[0]).toContainKeys(['_id', 'name', 'email', 'role', 'createdAt'])

I'm hoping that this gets fixed in this awesome-looking library in the next version, shouldn't be too difficult

@codejedi365
Copy link

codejedi365 commented Oct 31, 2021

DO NOT USE @ts-ignore to squash the errors

This is an absolute last resort feature that undermines type-safety and will introduce more problems than fix. Most errors come from a flaw in the tsconfig.json configuration or a missing type from the @types/<pkg> or package.json[types] declaration files.

INSTEAD

A better solution for Typescript + ts-jest + jest-extended is detailed in this Gist. The Gist provides a description of how to configure types detection, associated project organization, and integration with ts-jest.

Separately, in contradiction to this repository's README.md I do not recommend adding the jest-extended types reference to globals.d.ts with the rest of your project. This does not separate your tests from your source code and if you emit declaration files or other compiled files, it might include jest types. Additionally, if you happen to have a type that overlaps with jest-extended then your Typescript will not warn you that your type is not declared when it really references a jest-extended type. Instead, you should create a tsconfig.jest.json extension file specifically for ts-jest. This is similar to the extension files used for eslint, when you want to lint your test files but they are not included in your src directory. See more details in the gist.

@replete
Copy link

replete commented Nov 1, 2021

Thanks for the fine recommendation and explanation, @codejedi365. Sounds like you could easily fix the root issue in this relatively new project. I'm still quite new to TypeScript, so using // @ts-ignore was a quick fix, and one that assumes that soon the root cause would be fixed anyway.

Cheers

@codejedi365
Copy link

not a problem, @replete, thanks for the compliment. My apologies as it seems I came "off the top rope" on your comment. I was trying to make a point for all others in the channel but I don't think it came off that way as I re-read. My apologies.

Yes, @ts-ignore is a quick fix when you don't know what to do and it's easy to feel that way when jumping into Typescript. I remember it was about a year ago today when I first started TS. Recommend read, read, and read all the theory behind it (Medium.com has some helpful articles) and keep tinkering with it until you find how to make the compiler happy and work for you.

I'm hoping my Gist post does solve the most of the issues here, I was thankful it worked after hours of messing with it. I think the complexity of Typescript configurations is most to blame and the unknown need of all of us to update our jest config to the new naming convention used by this library. If there is a root issue that needs fixing, I might have time in a few weeks.

@replete
Copy link

replete commented Nov 1, 2021

Updated solution, removes need for setupTests.ts:

With this configuration: I have intellisense in vscode, jest tests run as expected, and there's no extra duplication, extra files, or //@ts-ignore

jest.config.js:

{
    // ...
    setupFilesAfterEnv: ['jest-extended/all']
}

./tsconfig.json:

{
    // ...
    "files": ["node_modules/jest-extended/types/index.d.s"]
}

@replete
Copy link

replete commented Nov 6, 2021

@ilyub Does the solution in comment above solve your problem?

@replete
Copy link

replete commented Nov 6, 2021

@mattphillips I'm not super familiar with this library, or jest, but it seems to me like adding this to the README.md somewhere would be useful for people running Typescript projects.

Unless this is only happening because of a bug...?

@deuscapturus
Copy link

None of the above fixed test/cdk.test.ts:4:33 - error TS2306: File '.../node_modules/jest-extended/types/index.d.ts' is not a module.

Ultimately the only fix was to downgrade jest-extended to v0.11.5

@codejedi365
Copy link

codejedi365 commented Nov 12, 2021

@deuscapturus can you provide more details? Post a link to a Gist that you upload the relevant files, and I'll take a look. Based on your error message it looks like you are importing types directly and that is likely going to have issues.

In the meantime, I recommend reviewing my post above with the Gist link.

@merrywhether
Copy link

merrywhether commented Nov 15, 2021

This was my fix:

// tsconfig.json (note that this shouldn't also be your build config)
  "include": [
    ...,
    "setupTestsAfterEnv.ts"
  ],
// jest.config.js
{
  ...,
  setupFilesAfterEnv: ['./setupTestsAfterEnv.ts'],
}
// setupTestsAfterEnv.ts

// import main for types & /all for matcher auto-registration
import 'jest-extended';
import 'jest-extended/all';

Importing jest-extended is all TS needs to get it to register the types, and you only have to import it once in your project. This does not however cause anything JS-related to actually happen, and you can't really work with the exports of jest-extended because they aren't in the type defs.

Importing jest-extended/all causes the auto-registration of all of the matchers, but doesn't do anything types-wise. TS will allow un-typed imports like that though, since the side-effects aren't modeled in TS's type system.

A bit of bummer to need the two imports, but it's easier than other solutions.

@codejedi365
Copy link

codejedi365 commented Nov 16, 2021

@merrywhether, how are you compiling your distribution code?

I don't recommend adding your setup.ts file to tsconfig.json[include] because if you set the outdir, for tsconfig, it will include that file into your distribution. Unless you are using webpack or another bundler that uses your entrypoint for workflow. A distribution that has your setup ts file that isn't directly referenced isn't valuable but it is a random irrelevant piece of code in your build.

This is why I recommend a tsconfig.jest.json which extends your base tsconfig but does not directly effect your real build configuration.

To clarify, what actually imports the types for jest-extended in your solution is your tsconfig.json[include]. The TS compiler evaluates included files and when accessing the setup file it sees the import statements so it follows them and finds the types. It is not the import at jest runtime.

@merrywhether
Copy link

merrywhether commented Nov 16, 2021

@codejedi365 Good callout on the distribution config, as ours is not standard:

  • tsconfig.json for dev work like type-checking tests/etc and integrating with IDEs (via default file-naming convention)
  • tsconfig.build.json which extends tsconfig.json but excludes tests/etc (which doesn't need filename convention because this is only used by builders for which it is easier to set the config path)

We also use babel for actual compilation (beyond type generation), so there's a lot of custom stuff going on that I should've specified. But it was late and I'd spent a bunch of time trying other fixes in the package to see if there was an easy PR; those all would've required a similar setup or restructuring of the package (TS does not make it easy to type raw imports without resorting to relying on have adjacent type files). But I updated my example above with a note.

As far as what actually imports the types, it is definitely the import 'jest-extended' line (commenting that out gives me type errors again). It is technically a combination of doing that import in a file that TS is aware via the include, but without an import TS won't ever load the types for the library (we do the same thing for jest-enzyme as well, though its main import auto-executes).

@igorwessel
Copy link
Contributor

igorwessel commented Jan 24, 2022

Hi guys!

To resolve the issue of being able to do the named import even though it doesn't cause a runtime issue, the problem is in declaration file don't have named exports for jest-extended module.
types/index.d.ts

We can resolve adding each matcher to a module and exports.

declare module "jest-extended" {
	 /**
     * Use .toBeEmpty when checking if a String '', Array [], Object {} or Iterable (i.e. Map, Set) is empty.
     */
    export function toBeEmpty(): jest.CustomMatcherResult;
}

But we have a problem, I couldn't figure out how to make typescript just import the types into the imported method and merge with jest types, e.g:

import { toBeEmpty } from 'jest-extended';

expect.extend({
	toBeEmpty,
});

This will still import all matchers types, merge with jest and cause runtime issues for matchers don't extended.

@igorwessel
Copy link
Contributor

I managed to make the typings for the typescript work when we only use some matchers.
First I separated the declaration from two matchers and add in own folder of respective matcher.

//@file: types/index.d.ts
declare namespace jest {
  // noinspection JSUnusedGlobalSymbols
  interface Matchers<R> {
    /**
     * Note: Currently unimplemented
     * Passing assertion
     *
     * @param {String} message
     */
    pass(message: string): R;
   ...
  }
}

moved to src/matchers/pass/index.d.ts:

// @file: src/matchers/pass/index.d.ts <- not need this line
/// <reference types="jest" />

declare global {
  namespace jest {
    interface Matchers<R> {
      /**
       * Note: Currently unimplemented
       * Passing assertion
       *
       * @param {String} message
       */
      pass(message?: string): never;
    }
    interface Expect {
      /**
       * Note: Currently unimplemented
       * Passing assertion
       *
       * @param {String} message
       */
      pass(message?: string): void;
    }
  }
}

export declare function pass(expected: string, message?: string): jest.CustomMatcherResult;

And use in setup.ts

import { pass } from 'jest-extended/dist/matchers/pass'

expect.extend({
  pass
})

@file: tsconfig.json
{
  ...,
  include: ["./setup.ts", ...]
}

With this we can import only the declaration of this specific file, without importing everything else.

I believe that it is currently impossible to have exports named with the jest matcher, why if we use for example the strategy of re-exporting all files in an index.js, and import only we need, typescript will follow all the re-exported files and find the declaration files (*.d.ts), which will have method typing that you didn't import.

I believe it is better to remove the named exports.

@mattphillips
Copy link
Member

Hey all I've just opened #407 with an example project that demos how to setup jest-extended globally with Typescript.

Please let me know if this resolves the issues.

Note: From what I can tell there is a separate issue / confusion around manually importing the matchers and the types lining up. I think it may be worth separating this into a different issue.

@shahinghasemi
Copy link

My work around:
jest.config.js:

{ ...
  setupFilesAfterEnv: ['jest-extended/all']
...}

then created a new file called types/global.d.ts and put import 'jest-extended' into it then added it to the tsconfig file:

...
  "files": ["types/global.d.ts"],
...

Now typescript/vscode can find the types for intellisense.

@robinzimmermann
Copy link

Thanks for taking the time to post this, @shahinghasemi, your solution worked great for me!

@xenoterracide
Copy link

why is this such a pain with this library? I don't have problems with other libraries including generally just the jest types, this seems like a real issue still, using 3.0.1

it's not clear how to use a jest.config.ts with ts-jest, it's also not clear why this isn't a "module". Isn't there a fix that can be put into this library?

@xenoterracide
Copy link

I'm using yarn with pnp, ts-jest, and a jest-config.ts, not sure if anything else is relevent, but if sinon-chai for example can do this without significant configuration, then this doesn't impossible

@xenoterracide
Copy link

one thing I see different in the code between the sinon-chai definitely typed and this is

/// <reference types="chai" />
/// <reference types="sinon" />

import * as Sinon from 'sinon';

declare global {

    export namespace Chai {
...

vs just

/// <reference types="jest" />

declare namespace jest {

I'm going to play with this localy to see if it matters

@xenoterracide
Copy link

answers here may also be relevant (sorry for the comment spam, work firewall) https://stackoverflow.com/q/57132428/206466

@keeganwitt
Copy link
Collaborator

I'm thinking of removing that import.

One of the complications is @types/jest vs the built-in types in Jest and working with both.

@xenoterracide
Copy link

yeah, I'm not using @types/jest IIRC, peoples aren't supposed to be?

@xenoterracide
Copy link

after doing some more experimentation and thinking, I would think the file would need to have

import { MockInstance } from 'jest-mock'

declare module '@jest/globals' { ...

haven't gotten that working yet either though.

@xenoterracide
Copy link

can we reopen this regarding @jest/globals? because as far as I can see this jest-extended can't possibly work with that

@mattphillips
Copy link
Member

@xenoterracide have you tried the example setup outlined in #407?

@xenoterracide
Copy link

@mattphillips yes, in what world do you think that will work with @jest/globals? the use case for which is to import the globals into your test file.

import { beforeEach, describe, expect, jest, test } from '@jest/globals'

@mattphillips
Copy link
Member

@xenoterracide I really don’t like the way you are choosing to communicate on this issue and the confrontational language being used - I’d ask you to show a little more respect when asking for help.

I’ve read your comments and am still none the wiser what you’re trying to do, what version of jest you’re using or the issue you’re actually having. So far you’ve just mentioned various technologies that have nothing to do with this project. For us to be able to help you, you would need to write a clear issue of your problem with plenty of context and ideally attach a repro (a repository containing a reproduction of the issue as close to your setup as possible).

FYI in the event you cannot reframe from a negative response, please feel free to click the fork button at the top of this repo and solve your problem yourself and maintain a fork.

@xenoterracide
Copy link

@jest/globals is part of jest it's the way jest itself is supporting types.

├─ @cof/e1-devexchange-client@workspace:packages/devexchange-client
│  └─ jest@npm:28.1.3 [aec87] (via npm:^28.1.3 [aec87])
│
├─ @cof/e1-devexchange-client@workspace:packages/devexchange-client
│  └─ @jest/globals@npm:28.1.3 (via npm:^28.1.3)
└─ @cof/e1-transient-fault-handler@workspace:packages/transient-fault-handler
   └─ jest-extended@npm:3.0.1 [d67c0] (via npm:^3.0.1 [d67c0])

System:

  • OS: macOS 11.6.6
  • CPU: (12) x64 Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz

Binaries:

  • Node: 16.14.2 - /private/var/folders/xk/s3n8ww014pzbx2sn30ctz7000000gq/T/xfs-2c31da68/node
  • Yarn: 3.2.1 - /private/var/folders/xk/s3n8ww014pzbx2sn30ctz7000000gq/T/xfs-2c31da68/yarn
  • npm: 8.5.0 - ~/.asdf/plugins/nodejs/shims/npm

@xenoterracide
Copy link

└─ @cof/e1-transient-fault-handler@workspace:packages/transient-fault-handler
   └─ @types/jest@npm:28.1.6 (via npm:^28.1.6)

@xenoterracide
Copy link

{
  "compilerOptions": {
    "esModuleInterop": true,
    "target": "es2015",
    "moduleResolution": "node",
    "sourceMap": true,
    "lib": ["es2018"],
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "declaration": true,
    "baseUrl": ".",
    "module": "CommonJS",
    "allowJs": false,
    "checkJs": false,
    "noImplicitAny": true,
    "declarationMap": true,
    "strict": true,
    "skipLibCheck": false,
    "strictNullChecks": true,
    "resolveJsonModule": true,
    "forceConsistentCasingInFileNames": true,
    "isolatedModules": true,
    "noEmit": true
  },
  "exclude": ["**/node_modules", "**/dist", "**/.test"],
  "files": []
}

@keeganwitt
Copy link
Collaborator

keeganwitt commented Aug 5, 2022

@xenoterracide Is what you are saying that if you don't use @types/jest, then when you try to do

import { expect } from '@jest/globals';
import * as matchers from 'jest-extended';
expect.extend(matchers);

Then the import from 'jest-extended' fails with something like

File 'C:/Users/Keegan/Documents/Projects/jest-extended_ts-jest_test/node_modules/jest-extended/types/index.d.ts' is not a module.ts(2306)

in VS Code?

@xenoterracide
Copy link

@keeganwitt well firstly, I don't think @types/jest should be a thing anymore, just FYI. It just supplies what jest is doing right now, yes? so it seems like it should be replaced. Also, it's possible, and/or it's possible I couldn't figure out how to even make that happen correctly with ts-jest. I can try that again tomorrow to see why I couldn't make that happen if you'd like. I do feel like importing jest-extended into every file is not going to be great.

@keeganwitt
Copy link
Collaborator

keeganwitt commented Aug 5, 2022

I'll just mention I use ts-jest with @types/jest all the time without importing jest-extended into every file, but that is using setupFilesAfterEnv. If that's not working for you, there very well could be a misconfiguration in your project. The reason it's done this way is not about typing, but because Jest won't load all matchers every time without being told to do so.

As for the usage of @types/jest in general. Eventually, yes, I think it will go away. But I'm not sure that's recommended just yet. They are not identical currently and last I heard there were some things needing improvement (though that might be out of date information now).

@xenoterracide
Copy link

It's recommended by jest, see the first sentence of that documentation.

@xenoterracide
Copy link

Also my problem is the test won't compile... Because it doesn't recognize the extra matchers. It'd be great if the problem was runtime.

@keeganwitt
Copy link
Collaborator

I was thinking of https://jestjs.io/docs/getting-started#type-definitions. I'm not sure which page you're referring to. If you don't want compile time checks of your tests, don't use ts-jest.

@cannontrodder
Copy link

After much frustration and experimentation with the above, I found I only had to add the package and then add:

"setupFilesAfterEnv": ["jest-extended/all"],

to my jest config in package.json and then restarting vscode

Without the last step, you play a game of whack-a-mole with the various different errors above and the suggested fixes.

@Codex-
Copy link
Contributor

Codex- commented Feb 3, 2023

Opened #559 to resolve the original error.

@ngouy
Copy link

ngouy commented Jan 2, 2024

On my end my issue was I wasn't adding the

"setupFilesAfterEnv": ["jest-extended/all"]

to all my jest configs

One of those config were hidden inline the package.json file

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet