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

fix: update spy type inference #643

Merged
merged 7 commits into from Feb 1, 2022

Conversation

DerYeger
Copy link
Member

@DerYeger DerYeger commented Jan 27, 2022

Closes #637.

I'm pretty sure this should solve the error.
If I understand the current typing correctly, a method was required to be a constructor.
This should remove that constraint.
If we do want to spy on constructors I can add another overload declaration.

@netlify
Copy link

netlify bot commented Jan 27, 2022

✔️ Deploy Preview for vitest-dev ready!

🔨 Explore the source changes: a94601e

🔍 Inspect the deploy log: https://app.netlify.com/sites/vitest-dev/deploys/61f901e67b2bab00070e2c46

😎 Browse the preview: https://deploy-preview-643--vitest-dev.netlify.app

@DerYeger DerYeger added the bug label Jan 27, 2022
@@ -120,7 +120,7 @@ export function spyOn<T, G extends Properties<Required<T>>>(
export function spyOn<T, M extends Classes<Required<T>>>(
object: T,
method: M
): Required<T>[M] extends new (...args: infer A) => infer R
): Required<T>[M] extends (...args: infer A) => infer R
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't this change make this type the same as Methods case on line 126?

From the type info it looks like you can spy on instance methods by spying on the constructor. I'm not sure if this is the case with tinyspy.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it will. Maybe we need to change the order or something

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For context: we didn't have this overload, but added it in some issue to mock classes. This aligns with jest and works well, since classes are functions.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we need to change the order or something

Probably need to use conditional types to merge those two cases into one.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think Classes returns incorrect types here, since the idea here is to limit overload. never should never happen here in theory, so conditional types wouldn't be needed.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I missed that part, my bad.

I'm not too familiar with TypeScript's conditional types.
Is it possible to check if it extends a union type? Or maybe something along the lines of:

(Required<T>[M] extends (...args: infer A) => infer R) || (Required<T>[M] extends new (...args: infer A) => infer R) ? SpyInstance<A, R> : never

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I missed that part, my bad.

I'm not too familiar with TypeScript's conditional types. Is it possible to check if it extends a union type?

You need to chain them like so:

A extends B 
  ? B extends C 
    ? true
    : false
  : never

It's really ugly. But as I said it shouldn't be needed, since M extends Classes<Required<T>>, so Required<T>[M] should only contain constructors. extends (...args: infer A) => infer R is just a formality to get types of arguments and return type.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Classes type is strange here. It is a dictionary of constructors for some reason. Am I missing something?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I pushed a commit that should just merge the two overloads in a backward-compatible way.

@DerYeger
Copy link
Member Author

Rebasing broke the spy test. Did something change since my initial branching?

@Demivan
Copy link
Member

Demivan commented Jan 28, 2022

Rebasing broke the spy test. Did something change since my initial branching?

Yes, there was a fix for #645 in tinyspy. Will check this case in tinyspy.

@sheremet-va
Copy link
Member

Maybe this is another bug. localStorage and widow.localStorage are not the same?

I know we have a similar issue with timers. Can you try assigning global.window to global?

@DerYeger
Copy link
Member Author

DerYeger commented Jan 28, 2022

It worked before the rebase.

Are you sure that they are not the same using jsdom?

I'm pretty sure that they are the same object, as window is basically equal to global here.

@Demivan
Copy link
Member

Demivan commented Jan 28, 2022

Are you sure that they are not the same using jsdom?

Just checked it in tinyspy repo. It works with happy-dom but not with jsdom. And it does not work even when using it without window.. So something actually broke.

@DerYeger
Copy link
Member Author

Are you sure that they are not the same using jsdom?

Just checked it in tinyspy repo. It works with happy-dom but not with jsdom. And it does not work even when using it without window.. So something actually broke.

I'm glad that the test was useful for something else than the typecheck after all

@Demivan
Copy link
Member

Demivan commented Jan 28, 2022

@DerYeger tinylibs/tinyspy#18 (comment) - check my comment on tinyspy. We cannot mock localStorage like this.

@DerYeger
Copy link
Member Author

@DerYeger tinylibs/tinyspy#18 (comment) - check my comment on tinyspy. We cannot mock localStorage like this.

Hmm, weird that it worked before

@Demivan
Copy link
Member

Demivan commented Jan 28, 2022

tinyspy was mocking prototype but this caused other issues. As @sheremet-va stated tinylibs/tinyspy#18 (comment) you can spy on the prototype explicitly.

@Demivan
Copy link
Member

Demivan commented Jan 28, 2022

@DerYeger Can you disable that failing custom reporters test? I will look into how to write it properly later

@DerYeger
Copy link
Member Author

@DerYeger Can you disable that failing custom reporters test? I will look into how to write it properly later

Done

@patak-dev patak-dev merged commit 691bede into vitest-dev:main Feb 1, 2022
chaii3 pushed a commit to chaii3/vitest that referenced this pull request May 13, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

type: vi.spyOn(window.localStorage, 'getItem') inferred as never
4 participants