JS/TS运行时扩展Svelte组件的类型与实现问题
Svelte组件扩展问题解答
问题1:无需为所有必填属性设默认值也能正常运行?
你之前的代码用Omit<ComponentProps<T>, keyof typeof props>移除了默认属性的类型定义,导致原组件的其他必填属性被标记为可选,触发TypeScript错误。正确的做法是保留原组件的完整属性类型,允许用户传入所有必填/可选属性,同时用默认值兜底。
修改单个组件的代码:
class PrimaryButton extends Button { constructor(options: ComponentConstructorOptions<ComponentProps<Button>>) { // 默认值在前,用户传入的props在后,确保用户配置优先级更高 super({...options, props: {variant: 'orange', ...options.props}}) } }
这样原组件的必填属性仍会被TypeScript校验,用户使用时必须传入,不会出现错误。
问题2:创建带正确类型的通用extendComponent函数
之前的泛型定义没有准确推导扩展后组件的属性类型,下面是修正后的通用函数,TS会自动推导默认props的类型,并正确标记可选/必填属性:
import type { ComponentType, ComponentConstructorOptions, ComponentProps, SvelteComponent } from 'svelte'; export function extendComponent<T extends SvelteComponent, D extends Partial<ComponentProps<T>>>( Comp: ComponentType<T>, defaultProps: D ) { return class ExtendedComponent extends Comp { constructor(options: ComponentConstructorOptions< // 原组件属性中,被默认props覆盖的字段变为可选,其余保持原状态 Omit<ComponentProps<T>, keyof D> & Partial<Pick<ComponentProps<T>, keyof D>> >) { super({ ...options, props: { ...defaultProps, ...options.props } }); } } as ComponentType< SvelteComponent<Omit<ComponentProps<T>, keyof D> & Partial<Pick<ComponentProps<T>, keyof D>>> >; } // 使用示例:无需手动指定泛型,TS自动推导 const PrimaryButton = extendComponent(Button, { variant: 'orange' });
扩展后的组件会将variant标记为可选(因为已提供默认值),其他属性保持原组件的必填/可选规则。
问题3:为on:click设置默认事件处理
可以实现,但Svelte的事件监听不属于组件props,需要通过构造函数的events选项合并默认事件和用户传入的事件。修改通用函数支持默认事件:
import type { ComponentType, ComponentConstructorOptions, ComponentProps, SvelteComponent, ComponentEvents } from 'svelte'; export function extendComponent<T extends SvelteComponent, D extends Partial<ComponentProps<T>>, E extends Partial<ComponentEvents<T>>>( Comp: ComponentType<T>, defaultProps: D = {} as D, defaultEvents: E = {} as E ) { return class ExtendedComponent extends Comp { constructor(options: ComponentConstructorOptions< Omit<ComponentProps<T>, keyof D> & Partial<Pick<ComponentProps<T>, keyof D>> >) { // 合并事件:先执行默认事件,再执行用户事件(顺序可按需调整) const mergedEvents = { ...defaultEvents }; for (const [eventName, userHandler] of Object.entries(options.events || {})) { mergedEvents[eventName as keyof E] = (event: any) => { // 执行默认事件 (defaultEvents[eventName as keyof E] as any)?.(event); // 执行用户自定义事件 userHandler(event); }; } super({ ...options, props: { ...defaultProps, ...options.props }, events: mergedEvents }); } } as ComponentType< SvelteComponent<Omit<ComponentProps<T>, keyof D> & Partial<Pick<ComponentProps<T>, keyof D>>> >; } // 使用示例:给PrimaryButton添加默认click事件 const PrimaryButton = extendComponent(Button, { variant: 'orange' }, { click: (event) => console.log('默认点击事件触发', event) });
用户使用时传入的on:click会在默认事件之后执行,若需要优先执行用户事件,调换两者的调用顺序即可。
内容的提问来源于stack exchange,提问作者Eric Kim
相关产品推荐
相关产品推荐

