vitest
vitest copied to clipboard
fix: switch ExpectStatic `any` types to `AsymmetricMatcher<unknown>`, with `DeeplyAllowMatchers<T>`
Description
Switches the two any types to ~~unknown~~ AsymmetricMatcher<unknown>, and updated APIs such as toEqual<T> to deeply allow those using a new DeeplyAllowMatchers<T>.
Sending as a draft for reference.
Please don't delete this checklist! Before submitting the PR, please make sure you do the following:
- [x] It's really useful if your PR references an issue where it is discussed ahead of time. If the feature is substantial or introduces breaking changes without a discussion, PR might be closed.
- [x] Ideally, include a test that fails without this PR but passes with it.
- ~~I don't know how to best do this. Do you want a type test somewhere?~~ I added a
types.test.tsfile but am 100% sure I missed a much better way to do that
- ~~I don't know how to best do this. Do you want a type test somewhere?~~ I added a
- [x] Please, don't make changes to
pnpm-lock.yamlunless you introduce a new test example.
Tests
- [x] Run the tests with
pnpm test:ci.
Documentation
- [x] If you introduce new functionality, document it. You can run documentation with
pnpm run docscommand.
Changesets
- [x] Changes in changelog are generated from PR name. Please, make sure that it explains your changes in an understandable manner. Please, prefix changeset messages with
feat:,fix:,perf:,docs:, orchore:.
Deploy Preview for vitest-dev ready!
Built without sensitive environment variables
| Name | Link |
|---|---|
| Latest commit | 3ce8784f9b83f59e51b8c82f8924430c8b43ea3a |
| Latest deploy log | https://app.netlify.com/sites/vitest-dev/deploys/674f4d9078b4770008db36bb |
| Deploy Preview | https://deploy-preview-7016--vitest-dev.netlify.app |
| Preview on mobile | Toggle QR Code...Use your smartphone camera to open QR code link. |
To edit notification comments on pull requests, go to your Netlify site configuration.
How is this going to work?
expect(obj).toEqual<{
id: string
name: string
}>({
id: expect.any(String),
name: 'Amelia',
})
Error:
Type 'unknown' is not assignable to type 'string'.ts(2322)
How is this going to work?
expect(obj).toEqual<{ id: string name: string }>({ id: expect.any(String), name: 'Amelia', })Error:
Type 'unknown' is not assignable to type 'string'.ts(2322)
Aha! Updated to use a DeeplyAllowMatchers.
Hey @JoshuaKGoldberg! I'd love to include this in 3.2. Could you add some tests to test/typescript? Or should I take over this PR? Thank you!
That's great, thank you @sheremet-va! Yes please - I'm a bit swamped this month and don't think I'll have time to figure out the test/typescript directory & add tests soon.
Hello Vite! I just bumped to 3.2.0 and I ran into the same problem as https://github.com/vitest-dev/vitest/pull/7016#issuecomment-2517674066
Here is my code (details omitted for clarity):
const expectedEventDraft: EventDraft = {
timestamp: expect.any(Number)
};
expect(onSubmit).toHaveBeenCalledWith(expectedEventDraft);
type EventDraft = {
readonly timestamp: number;
// ... other uninteresting properties
}
With an error:
TS2322: Type AsymmetricMatcher<unknown, MatcherState> is not assignable to type number
What is the correct way to type this? I tried to use DeeplyAllowMatchers but it doesn't seem to be exported.
What is the correct way to type this? I tried to use
DeeplyAllowMatchersbut it doesn't seem to be exported.
Looks like we should expose it, I will create a PR
I feel this change is breaking some legit types starting at Vitest 3.2:
function expectMany<T>(value: { enabled: false } | { enable: true; data: T }) {
// In Vitest 3.2:
//
// Argument of type '{ enabled: false; } | { enable: true; data: T; }' is not assignable to parameter of type '{ enabled: false | AsymmetricMatcher<unknown, MatcherState>; } | { enable: true | AsymmetricMatcher<unknown, MatcherState>; data: AsymmetricMatcher<...> | DeeplyAllowMatchers<...>; }'.
// Type '{ enable: true; data: T; }' is not assignable to type '{ enabled: false | AsymmetricMatcher<unknown, MatcherState>; } | { enable: true | AsymmetricMatcher<unknown, MatcherState>; data: AsymmetricMatcher<...> | DeeplyAllowMatchers<...>; }'.
// Type '{ enable: true; data: T; }' is not assignable to type '{ enable: true | AsymmetricMatcher<unknown, MatcherState>; data: AsymmetricMatcher<unknown, MatcherState> | DeeplyAllowMatchers<T>; }'.
// Types of property 'data' are incompatible.
// Type 'T' is not assignable to type 'AsymmetricMatcher<unknown, MatcherState> | DeeplyAllowMatchers<T>'.
// Type 'T' is not assignable to type 'AsymmetricMatcher<unknown, MatcherState>'.ts(2345)
expect(value).toEqual(value);
}
We heavily use the satisfies keyword in our tests. This change seems to make it impossible to use it.
An abstract example, which demonstrates the problem:
type Foo = {
bar: string;
foobar: {
foo: string;
bar: string;
}[]
}
const mockedFoo = vi.mocked(mock<Foo>()) // mock is from https://github.com/eratio08/vitest-mock-extended - but the important part is the expect statement
expect(mockedFoo).toHaveBeenCalledWith({
bar: "baz",
foobar: expect.any(Array),
} satisfies Foo)
The error:
Type 'AsymmetricMatcher<unknown, MatcherState>' is missing the following properties from type....
We have some tests that pass expect.anything() to a constructor, now it is broken too. Typechecking now reports error of
TypeCheckError: Argument of type 'AsymmetricMatcher<unknown, MatcherState>' is not assignable to parameter of type 'AvroReadable'. Type 'AsymmetricMatcher<unknown, MatcherState>' is missing the following properties from type 'AvroReadable' position, read
Were we using expect.anything() improperly?
const avroReaderStub = new AvroReader(expect.anything());
vi.mocked(avroReaderStub.hasNext).mockReturnValue(true);
const chunk = new Chunk(avroReaderStub as any, 0, 0, "log/00/2020/07/30/2300/");
assert.equal(chunk.hasNext(), true);
vi.mocked(avroReaderStub.hasNext).mockReturnValue(false);
assert.equal(chunk.hasNext(), false);
https://github.com/Azure/azure-sdk-for-js/blob/6b4ce4831b0c572226f7de89c7c6f38ff325d415/sdk/storage/storage-blob-changefeed/test/chunk.spec.ts#L50-L59
@jeremymeng Your usage doesn't look right. If it can just work by new AvroReader({} as any), then that's not how you use expect.anything() https://vitest.dev/api/expect.html#expect-anything
@hi-ogawa Thanks! Right, I don't think we care about the constructor argument for this test. I fixed the test by replacing expect.anything() with an object.