vue 组件类型转为 webComponent
- vue 组件封装为 webComponent 后,官方给的类型补齐方式会导致 emit 事件中参数的类型不对
declare module 'vue' {
interface GlobalComponents {
// 请务必在此处输入 Vue 组件类型
// (SomeComponent,*而不是* SomeElement)。
// 自定义元素的名称中需要连字符,
// 因此请在此处使用连字符元素名称。
'demo-component': typeof Component
}
}
- 使用如下泛型修正,生成 webComponent 的类型代替 typeof Component
import type {
AllowedComponentProps,
ComponentCustomProps,
ComponentOptionsMixin,
ComponentProvideOptions,
DefineComponent,
EmitFn,
ExtractDefaultPropTypes,
MethodOptions,
PublicProps,
VNodeProps,
ComputedOptions
} from 'vue'
/**
* 从组件实例中获取webComponent组件实例的$emit
* { emitSome2: (event: CustomEvent<[id: number, ids: string[]]>) => any; }
*/
type ExtractEmitEventsAsCustomEvents<T> = {
-readonly [K in keyof T as K extends `on${infer Rest}`
? T[K] extends ((...args: any[]) => any) | undefined
? Uncapitalize<Rest>
: never
: never]-?: T[K] extends ((...args: infer P) => any) | undefined
? (event: CustomEvent<P>) => any
: never
}
/**
* 传入组件类,返回webComponent类
* 1、修复了emit类型错误
*/
export type WebComponentConstructor<
T extends DefineComponent<
{},
{},
{},
ComputedOptions,
MethodOptions,
ComponentOptionsMixin,
ComponentOptionsMixin,
{},
string,
PublicProps,
any,
ExtractDefaultPropTypes<any>,
{},
{},
{},
string,
ComponentProvideOptions,
true,
{},
any
>
> = Omit<T, 'new'> & {
new (...args: any[]): Omit<InstanceType<T>, '$props' | '$emit'> & {
$props: TransformOnPropsToCustomEvent<InstanceType<T>['$props']>
$emit: EmitFn<ExtractEmitEventsAsCustomEvents<InstanceType<T>>>
}
}
/**
* 从实例的$props中获取webComponent组件实例的$props
* { readonly onEmitSome2?: ((event:CustomEvent<[number, string[]]>) => any) | undefined }
*/
type TransformOnPropsToCustomEvent<
T,
BaseProps = VNodeProps & AllowedComponentProps & ComponentCustomProps
> = {
[K in keyof T]: K extends `on${string}`
? T[K] extends ((...args: infer P) => any) | undefined
? K extends keyof BaseProps
? T[K] // 如果属于基础props,保持原样
: ((event: CustomEvent<P>) => any) | undefined // 转换为CustomEvent包裹的参数
: T[K]
: T[K]
}