如何在Svelte组件中结合数组属性使用$$Generic?
问题:Svelte中通过属性注入嵌套组件并保持强类型
我需要通过属性注入嵌套组件,同时保持强类型校验。插槽方案不适用,因为没法轻松切换要使用的组件——如果插槽名称支持动态的话倒是可行,但目前并不支持。
最初尝试的问题:$$Generic导致类型统一绑定到首个元素
尝试用$$Generic实现时,数组中所有元素的类型都会基于第一个元素推导,导致后续不同组件的类型报错:
Dynamic.svelte
<script lang="ts"> import type { ComponentProps, ComponentType, SvelteComponentTyped } from 'svelte' type Component = $$Generic<SvelteComponentTyped> type ComponentAndProps = { component: ComponentType<Component> props: ComponentProps<Component> } export let array: ComponentAndProps[] = [] </script> {#each array as item} <svelte:component this={item.component} {...item.props} /> {/each}
Test.svelte
<script lang="ts"> import Dynamic from './Dynamic.svelte' import A from './A.svelte' // export let a: string import B from './B.svelte' // export let b: string </script> <Dynamic array={[ { component: A, props: { a: 'work fine' }, }, { // ❌ 类型错误:'typeof B__SvelteComponent_' 不能赋值给 'ComponentType<A__SvelteComponent_>' component: B, props: { b: '' }, }, { component: A, props: { a: 'work fine too'} } ]} />
这里无法单独推导数组中每个元素的props类型,请问有什么解决思路?
解决方案:通过工具函数关联组件与属性
最终我通过一个泛型函数,将组件和对应的props做类型绑定,实现了强类型校验:
utils.ts
import type { ComponentProps, ComponentType } from 'svelte' export type ComponentAndProps = { component: ComponentType props: ComponentProps<InstanceType<ComponentType>> } export function component<Component extends ComponentType>( component: Component, props: ComponentProps<InstanceType<Component>> ): ComponentAndProps { return { component, props } }
修改后的Dynamic.svelte
<script lang="ts"> import type { ComponentAndProps } from '$lib/utils' export let array: ComponentAndProps[] = [] </script> {#each array as item} <svelte:component this={item.component} {...item.props} /> {/each}
修改后的Test.svelte
<script lang="ts"> import { component } from './utils' import Dynamic from './Dynamic.svelte' import A from './A.svelte' // export let a: string import B from './B.svelte' // export let b: string </script> <Dynamic array={[ component(A, {a: 'hey'}), component(B, {b: 'mom'}) ]} />
扩展:泛型场景的适配
这个方案同样适用于泛型组件场景,比如渲染表格行:
<script lang="ts"> import type { ComponentAndProps } from './utils' type Item = $$Generic export let item: Item export let fields: ((item: Item) => ComponentAndProps)[] </script> {#each fields as field} {@const { component, props } = field(item)} <svelte:component this={component} {...props} /> {/each}
内容的提问来源于stack exchange,提问作者Jonas
相关产品推荐
相关产品推荐

