11

I'm just trying to setup an array of components so I can do some dynamic component switching later. But I'm stuck at getting Typescript to work with it and no matter what way I ask the question online all the previous answers are all about completely different things.

I've provided a screenshot of what I want to do. I want to be able to write a typescript interface and give it some type which corresponds to any vue component I pass. It just needs to make sure that it's a vue component being passed, I don't care what component it is. I basically need a vue component type definition. How do I do that?

example of what I want

2
  • Try to hover a vue component and read what type the component is, then try to use it Commented Jul 26, 2022 at 15:27
  • First thing I tried. But Im not getting a whole lot out of it. Tried using ComponentPublicInstanceConstructor. But im not sure where to import it from or if its even right to use. Heres the text it gives me when hovering: const Navigation: ComponentPublicInstanceConstructor<{ $: ComponentInternalInstance; $data: {}; $props: Partial<{}> & Omit<Readonly<{} & {} & {}> & VNodeProps & AllowedComponentProps & ComponentCustomProps, never>; ... 10 more ... Commented Jul 26, 2022 at 16:10

2 Answers 2

31

Component is indeed the correct type, as you've attempted, but you need to import it before use:

<script setup lang="ts">
import type { Component } from 'vue'
⋮
interface Props {
  header?: Component
  body?: Component
  footer?: Component
}

const fragments: Props = {
  header: FragProfile,
  body: FragSubject,
  footer: TypeSection
}
</script>

demo

Sign up to request clarification or add additional context in comments.

2 Comments

The Component type seems to accept any object though, e.g. {header: {}, body: {}, footer: {}} also works. I'm not sure that there is a way to restrict the type to only actual components.
Apparently, a more strict way to type a field to accept only vue component is: componentName: DefineComponent<{}, {}, any> - which is a component, which accepts any props, has any slots, and returns any type from its setup function. It still allows ANY component, but at least it won't allow [], [1], {} , etc
0

Here is a detailed example of dynamic type-safe component loading as it is tricky to get right.

You can use generic Component<PropType> type to define components with similar parameters.

So for instance, to dynamically load all components of certain type you can use this loader function.

type FilterCompName = "EasyFilter"  | "NameFilter"
type Proptype = { filter: string }
type FilteredComponent = Component<Proptype>

function loader<FilteredComponent>(comp: FilterCompName ): FilteredComponent  {
  return defineAsyncComponent(() => import(`./comps/${comp}.vue`))
}

Then you will have mapping array

type FilterNames= "easyFilter"  | "nameFilter"
const comps: { [id in FilterNames]: FilteredComponent} = {
  "easyFilter": loader("EasyFilter"),
  "nameFilter": loader("NameFilter")
} as const

Then just define ref for name and computed for the dynamically loaded async component

const filterString= ref<string>("test")
const name = ref<FilterNames>("easyFilter")
const ViewComp: ComputedRef<FilteredComponent> = computed(() => comps[name .value])

Then in template section you get dynamic component with correctly typed propertis

<template>
   <component  :is="ViewComp" :filter="filterString" />
</template>

Here is a working example

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.