TypeScript中如何正确使用svelte:component并标注动态组件类型?
问题
我需要基于枚举值使用动态组件渲染对应视图,常规写法如下:
<svelte:component this={components[enumValue]} {...props} />
但这种写法下,即使没有传入组件要求的全部必填参数,也不会触发编译报错,问题示例:
<svelte:component this={components[enumValue]} />
目标是找到正确的动态组件类型标注方式,实现必填props的编译时校验。
参考:React下同类场景可通过如下写法实现类型校验,可作为预期效果参照:
const props: Props = {...} const components: Record<EnumType, React.FC<Props>> = { [EnumType.variant_1]: Component1, [EnumType.variant_2]: Component2, }
解决方案
Svelte 4 处理方案
Svelte 4 原生的<svelte:component>类型定义存在设计缺陷,无论如何给组件映射对象标注类型,编译器都不会对传入的props做和静态组件一致的强校验,必须通过封装泛型包装组件的方式实现类型安全:
- 先定义类型工具与类型安全的组件映射
// 提取Svelte组件Props类型的工具类型 type SvelteProps<T extends import('svelte').ComponentType> = T extends import('svelte').ComponentType<infer P> ? P : never; // 业务枚举定义 enum ViewEnum { Variant1 = 'variant1', Variant2 = 'variant2' } // 导入对应组件 import View1 from './View1.svelte'; import View2 from './View2.svelte'; // 定义所有动态组件的公共Props类型,若各组件Props不同可使用联合类型 type DynamicViewProps = SvelteProps<typeof View1> & SvelteProps<typeof View2>; // 用satisfies约束组件映射的类型,保留字面量类型的同时做类型校验 const viewMap = { [ViewEnum.Variant1]: View1, [ViewEnum.Variant2]: View2, } satisfies Record<ViewEnum, import('svelte').ComponentType<DynamicViewProps>>; - 封装泛型动态组件包装器
<!-- DynamicView.svelte --> <script lang="ts" generics="T extends ViewEnum"> export let viewType: T; // 强制要求传入当前选中组件对应的全部必填Props export let viewProps: SvelteProps<typeof viewMap[T]>; </script> <svelte:component this={viewMap[viewType]} {...viewProps} /> - 使用包装组件替代原生
<svelte:component>,即可获得编译时校验:<!-- 触发TS类型错误,提示缺少必填Props --> <DynamicView viewType={ViewEnum.Variant1} /> <!-- 校验通过 --> <DynamicView viewType={ViewEnum.Variant1} viewProps={{ /* 填入View1要求的全部必填参数 */ }} />
Svelte 5 原生支持
Svelte 5 重构了动态组件的类型校验逻辑,不需要额外封装包装组件,只要给组件映射对象通过satisfies标注正确类型,直接使用原生<svelte:component>时,编译器就会自动校验传入的props是否符合当前选中组件的要求。
注意:如果不同枚举值对应的组件Props存在差异,不要强行将所有组件标注为统一Props类型,应使用联合类型配合枚举判别,避免出现类型校验漏判、误判。
内容的提问来源于stack exchange,提问作者Vitali
相关产品推荐
相关产品推荐

