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鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature]: Replace @types/jest's expect implementation with @jest/expect #12424

Open
SimenB opened this issue Feb 18, 2022 · 17 comments
Open

[Feature]: Replace @types/jest's expect implementation with @jest/expect #12424

SimenB opened this issue Feb 18, 2022 · 17 comments

Comments

@SimenB
Copy link
Member

SimenB commented Feb 18, 2022

馃殌 Feature Proposal

People might not want to use the globals (expect, test etc.) Jest provides (either because of ESM or just a preference). And because Jest is written in TypeScript, when doing e.g. import {expect} from '@jest/globals' all built in matchers are typed. However, if you use some matchers from the community (such as jest-extended), they augment the matchers from @types/jest instead of expect (or @jest/expect). If instead all libraries augmented the actual expect types, the imported matcher would be typed correctly.

However, this flips the problem - now people using the globals no longer get custom matchers typed! The solution to this problem is to make @jest/types use @jest/expect instead of shipping its own types for this.

This issue is for tracking the work that needs to be done before releasing Jest 28 stable so there's a single source of truth - the source code.


I have started: https://github.com/SimenB/DefinitelyTyped/tree/jest-expect

We need to make sure the tests pass. Most breakage is due to the tests using e.g. jest.InverseMatchers etc - I don't think that's super useful? I might be wrong though, in which case we should expose more of those types from expect and/or @jest/expect.

(note that running tests is "impossible" until microsoft/DefinitelyTyped-tools#411 is merged. I ran npm install manually inside types/jest to see errors in the tests in my IDE)

/cc @mrazauskas

@mrazauskas
Copy link
Contributor

mrazauskas commented Feb 18, 2022

Very good move. What I was playing with roughly looked like this:

  1. Types of all globals are exported from 'jest' (instead of '@jest/globals')
  2. index.d.ts inside '@types/jest':
declare const jest: import('jest').Jest;

declare const beforeAll: import('jest').TestFrameworkGlobals['beforeAll'];
declare const beforeEach: import('jest').TestFrameworkGlobals['beforeEach'];

declare const afterAll: import('jest').TestFrameworkGlobals['afterAll'];
declare const afterEach: import('jest').TestFrameworkGlobals['afterEach'];

declare const describe: import('jest').TestFrameworkGlobals['describe'];
declare const fdescribe: import('jest').TestFrameworkGlobals['fdescribe'];
declare const xdescribe: import('jest').TestFrameworkGlobals['xdescribe'];

declare const test: import('jest').TestFrameworkGlobals['test'];
declare const xtest: import('jest').TestFrameworkGlobals['xtest'];

declare const it: import('jest').TestFrameworkGlobals['it'];
declare const fit: import('jest').TestFrameworkGlobals['fit'];
declare const xit: import('jest').TestFrameworkGlobals['xit'];

declare const expect: import('jest').JestExpect;
  1. package.json inside '@types/jest':
"version": "28.0.0",
"peerDependencies": {
    "jest": "^28.0.0",
  }

The idea was to turn 'jest' into the single source of truth. There is no way using Jest without installing 'jest', but it is possible to install different versions of 'jest', '@jest/globals' and '@types/jest'.

So importing import {expect, jest} from 'jest' or augmenting through '@types/jest' should reference same type definition which are installed with that particular version of Jest.

Might be there is still a chance that something would drift, or overlap, or go wrong. What you think? (;

@SimenB
Copy link
Member Author

SimenB commented Feb 18, 2022

Yeah, that's essentially what DefinitelyTyped/DefinitelyTyped#44365 does, and is what I want as end state. However, that loses a bunch of jest.Thing helpers, so not sure if it's the right move at this moment.

I'd like to start with just replacing parts of @types/jest, then if that works it can be "global only". And I'd like to start with expect since that's what custom matchers integrate with. 馃檪

@mrazauskas
Copy link
Contributor

mrazauskas commented Feb 18, 2022

Perhaps we could try it out with some @types/jest-28-alpha? It just augments globals. Should work. Or?

I was playing jest.Thing helpers as well. Very easy to have them (;

@mrazauskas
Copy link
Contributor

Couldn鈥檛 recall in which of branches I had them. Something like this seems to be working (importing from current exports):

declare const jest: import('@jest/environment').Jest;

declare namespace jest {
  export type MockedFunction<T> = import('jest-mock').MockedFunction<T>;
  export type MockedClass<T> = import('jest-mock').MockedClass<T>;
  // ...
}

@SimenB
Copy link
Member Author

SimenB commented Feb 18, 2022

Ah nice, that'd be awesome. Would also be cool if we had the same in Jest 馃檪

We should probably port over the type tests in DefinitivelyTyped to this repo well

@SimenB
Copy link
Member Author

SimenB commented Oct 24, 2022

For anyone following along, we've replace our own usage of @types/jest with the types exported from @jest/globals and an extra package sticking Jest's globals into TS's globals: #13344.

I'd still love for @types/jest to work with @jest/expect, but that might just be DefinitelyTyped/DefinitelyTyped#62037 or something like it instead of having steps on the way

@akphi
Copy link
Contributor

akphi commented Oct 26, 2022

@SimenB I think I stumbled on this issue since I want to make jest-extended to work with @jest/globals. With this change, does it mean we can do some updates to https://github.com/jest-community/jest-extended/blob/main/types/index.d.ts and jest-extended would work nicely with @jest/globals?

@SimenB
Copy link
Member Author

SimenB commented Oct 26, 2022

If this issue is resolved, yes 馃檪 As of now @types/jest doesn't get its matchers from expect

@adam-rocska
Copy link

This ticket is just like how I like a good bbq... low n slow...

@mrazauskas
Copy link
Contributor

Actually there is no need to have @types/jest and to use it for re-exports. TypeScript picks up types from the following declaration in package.json:

{
  "devDependencies": {
    "@types/jest": "npm:@jest/test-globals@*"
  }
}

At the moment @jest/test-globals is private and it needs better name too. But in general I find this solution simple and even obvious. Well.. That鈥檚 rather a discovery for me, to be honest ;D

@mrazauskas
Copy link
Contributor

mrazauskas commented Oct 4, 2023

@SimenB What you think about the above idea?

I was thinking @jest/test-globals could be simply published as @jest/globals. In this case, current exports of @jest/globals should be moved to jest.

And package.json looks like this:

{
  "devDependencies": {
    "@types/jest": "npm:@jest/globals@*"
  }
}

Ah.. Of course, @types/jest gets deprecated. The end (;


Here is somewhat similar solution I tried to put together: mattphillips/jest-expect-message#70

@SimenB
Copy link
Member Author

SimenB commented Oct 4, 2023

jest-runtime depends on @jest/globals, so we'd end up polluting the global namespace by default, which I wanna avoid.

But if we could end up exporting the globals from the jest package, that'd be cool

@mrazauskas
Copy link
Contributor

Right... Now I recall that we already talked about this in #12411

@SimenB
Copy link
Member Author

SimenB commented Oct 9, 2023

If we started publishing @jest/test-globals - would @types/jest be able to just import that and have the global environment augmented?

We're still missing some types etc., but easy enough to add any missing later

@mrazauskas
Copy link
Contributor

Importing @jest/test-globals into @types/jest is interesting idea, but it felt clumsy. This is because other @types libraries depend on @types/jest. The change is breaking for them.

In a way they should publish major releases. Does that mean that those libraries should also bump their majors? Will everyone agree? For instance, some @types/x-jest is referencing @types/jest. It supports Jest 27 and up. Reexported @jest/test-globals types means they have to give up supporting Jest 27 (or not)?

Might be I am overthinking. In any case, it sounded that:

"devDependencies": {
  "@types/jest": "npm:@jest/test-globals@*"
}

is a solution to provide global typings without even touching @types/jest package. This way @types/x-jest can choose to support @jest/test-globals at any time they find it comfortable.

@SimenB
Copy link
Member Author

SimenB commented Oct 9, 2023

Hmm, yeah. Good question.

Is it breaking in a way that we should fix? I guess we could also set the version in the deps of the packages that depend on @types/jest?

Or if the other packages on DT only e.g. add new matchers, they'd ideally extend expect types directly (much of what this issue is about). Then the other types from @types/jest can still live there, but new matchers work regardless of where they're coming from.

@mrazauskas
Copy link
Contributor

Hm.. if I get it right, the dependency on another @types package is set through /// <reference types="..." /> directive. Those are not versioned. Might be that is limitation of DT. Also can be I missed something.

A real case is here: https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/heft-jest

Here jest.mocked() is added through a @types package to avoid importing it from ts-jest.

The jest.mocked() which ships with Jest has different signature. Hence switching to the build-in Jest types is breaking. I opened microsoft/rushstack#3609 to find a solution, but there is no progress for a year.

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

No branches or pull requests

4 participants