Note on Typing Dynamic Vue Components with Typescript


Say you want to render a different component based on some condition

dynamicComponent(foo: Foo) {
  return foo.bar === 'baz' ? FunComponent : SeriousComponent
}

Which you could then use in your vue template like any other computed property, using the <component is:> style

<component :is="dynamicComponent(foo) />

This would render either the FunComponent or SeriousComponent based on a condition / property on object passed to the computed property that returns them.

How to type this was a mystery to me until recently when I found out that Vue is not the only exported member of /node_modules/vue

import Vue, { Component, AsyncComponent } from 'vue'

The computed property's return value can be types as:

dynamicComponent():
 | Component<any, any, any, any>
 | AsyncComponent<any, any, any, any>

Looking into the src from the import for component yields:

// we don't support infer props in async component
// N.B. ComponentOptions<V> is contravariant, the default generic should be bottom type

export type Component<Data=DefaultData<never>, Methods=DefaultMethods<never>, Computed=DefaultComputed, Props=DefaultProps> =
  | typeof Vue
  | FunctionalComponentOptions<Props>
  | ComponentOptions<never, Data, Methods, Computed, Props>

For AsyncComponentType:

export type AsyncComponent<Data=DefaultData<never>, Methods=DefaultMethods<never>, Computed=DefaultComputed, Props=DefaultProps>
  = AsyncComponentPromise<Data, Methods, Computed, Props>
  | AsyncComponentFactory<Data, Methods, Computed, Props>

Pretty cool. You can see more exported types at node_modules/vue/types/options.d.ts