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

chore(findComponent): refactor find & findComponent (fixes #716, #899) #1094

Merged
merged 2 commits into from
Nov 26, 2021

Conversation

xanf
Copy link
Collaborator

@xanf xanf commented Nov 22, 2021

Sorry for such a big one, I was working on typings and improving typings revealed multiple issues

Basically major changes in this PR are:

  • find now finds DOM nodes by ref (Support find({ ref }) syntax #716)
  • findAllComponents / findComponent / getComponent migrated to baseWrapper since they are now officially supported in both wrappers
  • major types rework for findComponent / findAllComponents with better type inference by large margin
  • better typing for .element - it was originally Element which is actually wrong - it could be Element, Comment (when rendering empty v-if), Text (if component renders just raw text). Fixed this typings and added guards everywhere
  • findComponent and findAllComponents are now silently returning DOMWrappers for functional components encountered. This mimics VTU v1 behavior and really helps with migration
  • add type tests for findComponent

@xanf xanf force-pushed the improve-find branch 3 times, most recently from 7c70668 to 977363e Compare November 22, 2021 01:42
name: string
length?: never
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This was a nasty one. { name: string } definition matches Function (because functions have name property) which messes with our type inference. Putting length to never allows us to distinguish between finding a function and finding by object with name field

Copy link
Member

Choose a reason for hiding this comment

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

damn, nasty but smart. thanks for the explanation, I would've missed it

'NestedChild'
)
expect(
wrapper.findAllComponents<DefineComponent>('.in-root')[0].vm.$options.name
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I like that when you're doing something nasty (working outside WrapperLike interface which is unified across VueWrapper and DOMWrapper - you need to explicitly specify "hey, I know that I'm doing, there will be no functional component here". Type safety as it is!

@xanf xanf marked this pull request as draft November 22, 2021 01:46
@xanf xanf force-pushed the improve-find branch 2 times, most recently from 572f3b6 to c795b0d Compare November 22, 2021 04:14
Comment on lines +20 to +26
T extends Omit<
ComponentPublicInstance,
'$emit' | keyof ComponentCustomProperties
> & {
$emit: (event: any, ...args: any[]) => void
} & ComponentCustomProperties = ComponentPublicInstance
> extends BaseWrapper<Node> {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

TypeScript as usually sucks in co- and contravariance, that's why we need to relax check on $emit a bit (in other words $emit generated by InstanceType<DefineComponent<...>> which is ComponentPublicInstance<...> is not a subclass of ComponentPublicInstance (with default params) due to params type mismatch in EmitFn (the code of this helper type in vue-next is a bit insane)

But I wish someone told me why i need to decompose & put back ComponentCustomProperties - or mount will fail typecheck otherwise 😕

@xanf xanf marked this pull request as ready for review November 22, 2021 04:23
@xanf
Copy link
Collaborator Author

xanf commented Nov 22, 2021

@cexbrayat will be great to have another pair of eyes, especially on TypeScript typings :)

@lmiller1990 it seems that I've occasionally implemented circuit-breaker similar to your one at 2700e88, so this commit kinda reverts that change, sorry (I think separate createWrapper approach is a bit cleaner)

* allow find also find DOM nodes by ref
* generalize findAll/findAll components behavior
* greatly improve typings
* add types tests for findComponent
Copy link
Member

@lmiller1990 lmiller1990 left a comment

Choose a reason for hiding this comment

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

Looks good - I don't fully see how the circular dep is cleaned up, but assuming if you do yarn build there is no warnings?

Left one minor comments. Could probably add more tsd tests if you like (or at a future time). Excited for this PR!

ComponentPublicInstance,
'$emit' | keyof ComponentCustomProperties
> & {
$emit: (event: any, ...args: any[]) => void
Copy link
Member

Choose a reason for hiding this comment

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

Should this be event: string?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Unfortunately no. ComponentPublicInstance<> (with all default parameters) infers $emit exactly as (event: string, ...args: any[]) => void. When you pass here a component with defined emits (for example hi) $emit will be (event: "hi") => void' which is not a subtype of (event: string, ...args: any[]) => void (event argument is too narrow). That's why we need to cheat with any

I've tried to add event: EmitType and EmitType extends string = string as parameter to VueWrapper generic to express this limitiation , but it seems typescript still infers wrong type

Copy link
Member

@cexbrayat cexbrayat left a comment

Choose a reason for hiding this comment

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

Nice work! To be honest, it's hard to review, but LGTM overall.

@pikax maybe you can take a look for the types part?

@lmiller1990 As this might introduce unwanted regressions/behavior differences, maybe we could release it on a special tag, before making it rc.18?

@xanf
Copy link
Collaborator Author

xanf commented Nov 24, 2021

Nice work! To be honest, it's hard to review, but LGTM overall.

Sorry for that, I promise not to do such big one in future 🤗. It was unexpectedly too interconnected

@lmiller1990 As this might introduce unwanted regressions/behavior differences, maybe we could release it on a special tag, before making it rc.18?

I would like not to do that. We have relatively small user base, so adding a special tag will not help that much, making sure it is even less tested. I've ran this change accross big suite (2k+ tests) with no changes, so I'm pretty confident in it except types part :)

Looks good - I don't fully see how the circular dep is cleaned up, but assuming if you do yarn build there is no warnings?

Sorry. I totally overlooked this one. I pushed separate commit e5e42dc to fix warning, WDYT of that?
This pattern (factory with runtime registration) feels a bit cleaner for me

@xanf xanf merged commit 58f8414 into master Nov 26, 2021
@xanf xanf deleted the improve-find branch November 26, 2021 15:56
@xanf xanf restored the improve-find branch November 26, 2021 21:53
@xanf xanf deleted the improve-find branch November 26, 2021 21:55
visualjerk added a commit to visualjerk/test-utils that referenced this pull request Feb 24, 2022
Since vuejs#1094, find supports element refs. This PR adds the new use case to the docs.
cexbrayat pushed a commit that referenced this pull request Feb 24, 2022
Since #1094, find supports element refs. This PR adds the new use case to the docs.
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.

4 participants