Vue 3 的 <script setup> 结合 TypeScript 编写泛型组件需在标签上用 generic 属性声明类型参数并加 extends 边界约束,使 props、emits、slots 和 expose 均自动继承泛型类型,提升复用性与类型安全。

Vue 3 的 <script setup> 结合 TypeScript 编写泛型组件,核心是用 generic 属性 声明类型参数,并让 props、emits、slots 和内部逻辑都基于该泛型做约束。这不是语法糖的“炫技”,而是真正提升组件复用性和类型安全的关键手段。
明确声明泛型参数
在 <script setup> 标签上添加 generic="T"(可多个,如 generic="T, U"),并推荐加上边界约束,避免类型失控:
<script setup lang="ts" generic="T extends { id: string | number; label: string }">- 这样传入的数组项、事件载荷、插槽作用域变量都能被 IDE 精准推导
- 不写
extends边界会导致T被推为unknown,失去类型提示
Props 和 Emits 自动适配泛型
defineProps 和 defineEmits 无需额外泛型标注,它们会自动从 script 的 generic 中继承类型参数:
-
const props = defineProps<{ items: T[]; disabled?: boolean }>();→props.items[0].id可点出、可校验 -
const emit = defineEmits<{ (e: 'select', item: T): void; (e: 'clear'): void }>();→ 调用emit('select', xxx)时,xxx必须满足T的结构
插槽与 expose 也受泛型影响
作用域插槽和 expose 暴露的方法同样能获得泛型上下文:
立即学习“前端免费学习笔记(深入)”;
- 模板中写
<slot :item="item" :index="i" />,父组件使用时v-slot="{ item }"中的item类型就是T -
defineExpose({ getValue: (): T => selectedItem.value });→ 外部通过ref调用getValue()得到的值类型即为T
避免常见错误
泛型组件容易掉进的坑,往往不是写法问题,而是设计意识偏差:
- 别用
any或Record<string, unknown>替代泛型——看似灵活,实则放弃编译期检查 - 不要在泛型组件内部硬编码具体类型(比如只处理
string[]),否则就失去了泛型意义 - 如果泛型约束复杂(如多字段联合类型),建议抽成独立 interface 并在 types.ts 中导出,便于复用和文档化


















