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]
}
posted on 2025-07-11 14:12  噬蛇之牙  阅读(8)  评论(0)    收藏  举报