vuejs / core

🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
https://vuejs.org/
MIT License
47.69k stars 8.33k forks source link

types: Component cannot be rendered with JSX #3224

Open KaelWD opened 3 years ago

KaelWD commented 3 years ago

Version

3.0.5

Reproduction link

https://codesandbox.io/s/boring-babbage-lgpd9?file=/src/main.tsx

Steps to reproduce

Try to compile declare const Foo: Component; return <Foo />

What is expected?

No errors. <Bar /> should complain about a missing id prop.

What is actually happening?

JSX element type 'Foo' does not have any construct or call signatures


This was broken with h(Foo) too until #3219

HcySunYang commented 3 years ago

Why not use defineComponent?

KaelWD commented 3 years ago

It's a component passed in to a function from the user. The DefineComponent type mangles required prop types by passing them through ExtractPropTypes again. Component uses ComponentPublicInstanceConstructor which isn't exported. I basically need FunctionalComponent<Props> | ComponentPublicInstanceConstructor<Props>

Update: { new (): ComponentPublicInstance<Props> } | FunctionalComponent<Props> seems to do what I want.

HcySunYang commented 3 years ago

I mean use defineComponent to create components

const Comp = defineComponent({ /* ... */ })

Comp always conforms to the use in TSX

KaelWD commented 3 years ago

We do, this is for a config that people can pass components into:

new Vuetify({
  iconSets: {
    fa5: FontAwesomeIcon
  }
})

I need a type that both:

use defineComponent to create components

Again I'm not the one creating these components, but defineComponent doesn't allow functional components. If you pass it a function it turns it into setup instead.

I ended up using { new (): ComponentPublicInstance<Props> } | FunctionalComponent<Props> for this but I'd expect the builtin Component type to just work without any messing around.

HcySunYang commented 3 years ago

I got your point

pikax commented 3 years ago

This is a tricky one, Component should be the most generic valid component type.

TSX requires a type with a constructor to be able to render, which causes problems because Component does not require a constructor, because {} is a valid vue component.

There's no good solution for this, I think the Component is the wrong type to be used with TSX don't know if adding a new type is ideal, but this would probably work:

type ComponentTSX<Props = {}> = Component<Props> & { new (): ComponentPublicInstance<Props> }

declare const Foo: ComponentTSX;
declare const Bar: ComponentTSX<{ id: string }>;

() => [<Foo />, <Foo id="ok" />, <Bar id="ok" />];
KaelWD commented 3 years ago

I think with that you can't do const Foo: ComponentTSX = props => h(...