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

Bug: Instance properties appear in components list #159

Closed
g-plane opened this issue Apr 26, 2021 · 13 comments
Closed

Bug: Instance properties appear in components list #159

g-plane opened this issue Apr 26, 2021 · 13 comments
Labels
enhancement New feature or request

Comments

@g-plane
Copy link
Contributor

g-plane commented Apr 26, 2021

图片

As the screenshot shows, instance properties shouldn't be showed in components completion list.

@johnsoncodehk
Copy link
Member

This is expected, because these ones is subtypes of FunctionalComponent.

// true
type IsComponent = typeof $emit extends FunctionalComponent<infer _> ? true : false;

@g-plane
Copy link
Contributor Author

g-plane commented Apr 26, 2021

So strange...Is there a workaround?

@johnsoncodehk
Copy link
Member

@pikax can you take a look to this problem?

@pikax
Copy link
Member

pikax commented Apr 26, 2021

@pikax can you take a look to this problem?

This is a bit tricky since FunctionalComponent kinda looks the same as the $emit

You can validate the functional component like this:

type IsComponent<T> = T extends FunctionalComponent<infer P>
  ? P extends object ? true : false // `P` should be always an object on a functional component, but on emit extends `string`
  : false

@johnsoncodehk
Copy link
Member

johnsoncodehk commented Apr 26, 2021

@pikax thanks, but that may not handle well, there have more other properties like $d, $el, $n... and these ones can be any other Prop type.

I'm not sure if this is appropriate, can we make some FunctionalComponent properties not optional to block these function type variables?
For example, maybe change __file?: string to __file: string | undefined.

@pikax
Copy link
Member

pikax commented Apr 26, 2021

I'm not sure if this is appropriate, can we make some FunctionalComponent properties not optional to block these function type variables?

I'm afraid that is not possible because that would break a simple function:

      function MyFunctionalComponent(p: { a: 1}) {
        return h('div', p.a)
      }
      type FunctionComponent = IsComponent<typeof MyFunctionalComponent>

This changes will affect users using Javascript

<template>
  <MyComp a="Hello there"></MyComp>
</template>

<script setup>
import { h } from 'vue'

function MyComp(p){
	return h('div', p.a)
}
</script>

@pikax thanks, but that may not handle well, there have more other properties like $d, $el, $n... and these ones can be any other Prop type.

I reckon this only affects the script setup, right?

EDIT: Probably this is a more correct component for your needs

type IsComponent<T> = T extends (p: any, ctx?: any) => VNode
  ? true
  : false

@johnsoncodehk
Copy link
Member

johnsoncodehk commented Apr 26, 2021

I reckon this only affects the script setup, right?

@pikax Unfortunately, widespread impact. 😅

Or can we narrow the FunctionComponent caller return type?

(props: P, ...): any -> (props: P, ...): VNode // or other

@pikax
Copy link
Member

pikax commented Apr 26, 2021

Can you give some examples on what's breaking on your side? I can think on ways to improve it, DefineComponent should be straight forwards to get the type.

You could also just make a "rule": Only the defineComponent (and components:[]) components are available on the template. Which I think is a reasonable request, and it wouldn't break the upstream users

@johnsoncodehk
Copy link
Member

@pikax of course, sorry I should give this first.

import { defineComponent, FunctionalComponent } from '@vue/runtime-core';

type ExtractFunctionalComponents<T> = { [K in keyof T as T[K] extends FunctionalComponent<infer _> ? K : never]: T[K] }
const component = defineComponent({
    setup() {
        return {
            foo: () => 123,
            bar: () => 'str',
        };
    }
});
const localComponents: ExtractFunctionalComponents<InstanceType<typeof component>>;
localComponents. // doing auto-complete here should not have `foo`, `bar`

This two way also can fix it:

EDIT: Probably this is a more correct component for your needs

type IsComponent<T> = T extends (p: any, ctx?: any) => VNode
  ? true
  : false

Or can we narrow the FunctionComponent caller return type?

(props: P, ...): any -> (props: P, ...): VNode // or other

If FunctionComponent is not easy to change, I think I can use the way 1.

@pikax
Copy link
Member

pikax commented Apr 26, 2021

Check this playground

foo and bar are valid components actually (if used in script setup)

@johnsoncodehk
Copy link
Member

@pikax I see... So actually I should keep this behavior. Thanks to check it!

@g-plane do you agree to close this issue?

@pikax
Copy link
Member

pikax commented Apr 26, 2021

I would exclude if the key starts with $

Something of sorts

type ExtractFunctionalComponents<T> = {
  [K in keyof T as K extends `$${string}`
    ? never
    : IsFunctionalComponent<T[K]> extends true
    ? K
    : T extends Component
    ? K
    : never]: T[K];
};

@johnsoncodehk
Copy link
Member

johnsoncodehk commented Apr 26, 2021

@pikax <$foo> report illegal tag name in vue compiler, so yes I can filter it, thanks again!

Fixed and will update to 0.24.4.

@johnsoncodehk johnsoncodehk added enhancement New feature or request and removed upstream labels Apr 26, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

3 participants