Skip to content

fix: switch ExpectStatic any types to AsymmetricMatcher<unknown>, with DeeplyAllowMatchers<T>#7016

Merged
sheremet-va merged 8 commits intovitest-dev:mainfrom
JoshuaKGoldberg:expect-static-any-to-unknown
May 18, 2025
Merged

fix: switch ExpectStatic any types to AsymmetricMatcher<unknown>, with DeeplyAllowMatchers<T>#7016
sheremet-va merged 8 commits intovitest-dev:mainfrom
JoshuaKGoldberg:expect-static-any-to-unknown

Conversation

@JoshuaKGoldberg
Copy link
Copy Markdown
Contributor

@JoshuaKGoldberg JoshuaKGoldberg commented Dec 3, 2024

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.

Fixes #7015.

Please don't delete this checklist! Before submitting the PR, please make sure you do the following:

  • 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.
  • 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.ts file but am 100% sure I missed a much better way to do that
  • Please, don't make changes to pnpm-lock.yaml unless you introduce a new test example.

Tests

  • Run the tests with pnpm test:ci.

Documentation

  • If you introduce new functionality, document it. You can run documentation with pnpm run docs command.

Changesets

  • 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:, or chore:.

@JoshuaKGoldberg JoshuaKGoldberg changed the title fix: switch ExpectSTatic any types to unknown fix: switch ExpectStatic any types to unknown Dec 3, 2024
@netlify
Copy link
Copy Markdown

netlify bot commented Dec 3, 2024

Deploy Preview for vitest-dev ready!

Built without sensitive environment variables

Name Link
🔨 Latest commit 3ce8784
🔍 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...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

@sheremet-va
Copy link
Copy Markdown
Member

sheremet-va commented Dec 4, 2024

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)

@JoshuaKGoldberg
Copy link
Copy Markdown
Contributor Author

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.

@JoshuaKGoldberg JoshuaKGoldberg changed the title fix: switch ExpectStatic any types to unknown fix: switch ExpectStatic any types to AsymmetricMatcher<unknown>, with DeeplyAllowMatchers<T> Dec 6, 2024
@hi-ogawa hi-ogawa mentioned this pull request Dec 8, 2024
6 tasks
@sheremet-va sheremet-va added the p2-nice-to-have Not breaking anything but nice to have (priority) label May 15, 2025
@sheremet-va
Copy link
Copy Markdown
Member

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!

@JoshuaKGoldberg JoshuaKGoldberg marked this pull request as ready for review May 16, 2025 15:00
@JoshuaKGoldberg
Copy link
Copy Markdown
Contributor Author

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.

@sheremet-va sheremet-va merged commit 8ec448d into vitest-dev:main May 18, 2025
11 of 13 checks passed
@PavelVanecek
Copy link
Copy Markdown

PavelVanecek commented Jun 2, 2025

Hello Vite! I just bumped to 3.2.0 and I ran into the same problem as #7016 (comment)

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.

@sheremet-va
Copy link
Copy Markdown
Member

What is the correct way to type this? I tried to use DeeplyAllowMatchers but it doesn't seem to be exported.

Looks like we should expose it, I will create a PR

@dubzzz
Copy link
Copy Markdown
Contributor

dubzzz commented Jun 3, 2025

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);
}

@WtfJoke
Copy link
Copy Markdown

WtfJoke commented Jun 4, 2025

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....

@jeremymeng
Copy link
Copy Markdown

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

@hi-ogawa
Copy link
Copy Markdown
Collaborator

hi-ogawa commented Jun 6, 2025

@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

@jeremymeng
Copy link
Copy Markdown

@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.

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

Labels

p2-nice-to-have Not breaking anything but nice to have (priority)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

expect.any and expect.anything should return something more specific than any

7 participants