Vue3+TS通用Tile组件Storybook类型错误修复咨询
问题描述
我基于Vue 3.4+TypeScript开发了一个Tile组件,初始代码里item属性类型为any。为实现组件通用复用,我将其改为泛型类型T,修改后出现类型错误:Property 'key' does not exist on type 'T'.ts-plugin(2339)。
给泛型添加约束T extends { key: any }后,item.key的类型错误解决了,但Storybook的故事代码又报类型不兼容:
Type '<T extends { key: any; }>(__VLS_props: { "onClick-infobutton"?: (() => any) | undefined; "onUpdate:modelValue"?: ((modelValue: T[]) => any) | undefined; modelValue: T[]; name: string; label?: string | undefined; ... 10 more ...; item: T; } & VNodeProps & AllowedComponentProps & ComponentCustomProps, __VLS_ctx?: { .....' has no properties in common with type 'Omit<ConcreteComponent, "props">'.ts(2559) ...
需求:保持组件通用可复用(可传入Appliance这类任意带key的对象),同时解决Storybook的报错问题。
解决方案
1. 调整组件泛型定义方式
Vue 3.4的<script setup>泛型语法在Storybook中可能存在类型推导问题,可换用defineProps的泛型约束来实现,避免使用<script generic>属性:
修改组件的<script setup>部分:
<script lang="ts" setup> import ErrorMessage from '@/components/display/ErrorMessage.vue' import FormControl from '@/components/layout/FormControl.vue' import { computed } from 'vue' import useInput from '@/composable/common/use-input' import TileCardContent from '@/components/common/tile/TileCardContent.vue' // 定义带key的基础类型 type WithKey = { key: string | number } // 用泛型约束props,确保item包含key属性 const props = defineProps<{ name: string label?: string inlineContent?: boolean item: WithKey & Record<string, any> // 兼容任意带key的扩展对象 }>() // 定义model值为带key的对象数组 const modelValue = defineModel<WithKey[]>({ required: true }) const { errors, value, isValid, errorMessagesId } = useInput<WithKey[]>(props.name) const checked = computed({ get() { return value.value ? value.value.some(item => item.key === props.item.key) : false; }, set(checked: boolean) { if (!value.value) { value.value = []; } if (checked) { value.value.push(props.item); } else { value.value = value.value.filter(item => item.key !== props.item.key); } } }); function toggleModelValue() { checked.value = !checked.value } </script>
2. 显式声明组件泛型类型(保留泛型的方案)
如果要严格保留泛型定义,可在组件文件中添加额外的类型声明,帮助Storybook识别泛型组件:
在组件文件末尾添加独立的<script>标签:
<script lang="ts"> import type { DefineComponent } from 'vue' // 声明泛型组件类型 export type TileProps<T extends { key: any }> = { name: string label?: string inlineContent?: boolean item: T } type TileComponent<T extends { key: any }> = DefineComponent<TileProps<T>> // 导出组件类型供Storybook使用 export default {} as TileComponent<any> </script>
然后在Storybook代码中显式指定泛型参数:
import type { Meta, StoryObj } from '@storybook/vue3' import Tile, type { TileComponent } from './Tile.vue' import * as yup from 'yup' import { useField } from 'vee-validate' import { ref, watch, onMounted } from 'vue' import { withCodeSnippet } from '../../../../.storybook/decorator' type Appliance = { key: string coefficient: number } // 显式指定Tile的泛型为Appliance const meta: Meta<TileComponent<Appliance>> = { component: Tile, } export default meta type Story = StoryObj<TileComponent<Appliance>> export const Default: Story = { name: 'With validation', render: (args) => ({ components: { Tile }, setup() { const appliances: Appliance[] = [ { key: 'appliance1', coefficient: 10 }, { key: 'appliance2', coefficient: 20 }, { key: 'appliance3', coefficient: 30 }, { key: 'appliance4', coefficient: 40 }, { key: 'appliance5', coefficient: 50 } ] const modelValue = ref<Appliance[]>([]) const { errors, value, validate } = useField<Appliance[]>( 'appliances', yup .array() .of( yup.object().shape({ key: yup.string().required(), coefficient: yup.number().required() }) ) .required() .min(1) ) watch( modelValue, (newValue) => { value.value = newValue validate() }, { deep: true } ) onMounted(async () => { await validate() }) return { appliances, modelValue, errors, value } }, template: ` <div class="ods__d-grid ods__gap ods__g-cols-2"> <div v-for="appliance in appliances" :key="appliance.key"> <Tile v-model="modelValue" :item="appliance" :label="appliance.key" :name="appliance.key" /> </div> </div> <p v-if="errors">Errors: {{ errors }}</p> <p>Selected Appliances: {{ modelValue }}</p> ` }) }
问题根源
Storybook对Vue 3 <script setup>的generic属性类型推导支持不完善,无法自动识别泛型组件的参数结构。通过调整泛型定义方式或显式声明组件类型,可让Storybook正确解析组件的类型约束,解决类型不兼容报错。
内容的提问来源于stack exchange,提问作者Aya Abdessalem
相关产品推荐
相关产品推荐

