Vue 3 + TypeScript 中单文件组件的 Props 泛型支持需通过 defineComponent 和 defineProps 显式泛型声明实现,推荐先定义泛型接口再传入 defineProps,配合 withDefaults 保持泛型完整性,并可提取为独立类型文件复用。

Vue 3 + TypeScript 中,单文件组件(SFC)的 Props 泛型支持需要借助 defineComponent 和 defineProps 的显式泛型声明方式实现。直接在 <script setup> 中用 defineProps<T>() 是无效的——TypeScript 不允许泛型参数直接用于调用签名,必须通过类型参数推导或 as const + 类型断言等间接方式实现。
使用 defineProps 配合泛型接口(推荐)
最清晰、可维护的方式是先定义一个泛型接口,再将其传给 defineProps:
- 在
<script setup lang="ts">中,声明一个带类型参数的接口,如Props<t></t> - 用
defineProps<props>>()</props>显式指定具体类型 - 这样既保留类型安全,又支持子组件接收不同类型的 props(如
data: T[]、formatter: (v: T) => string)
示例:
interface Props<T> {
items: T[];
keyName?: string;
formatter?: (item: T) => string;
}
const props = defineProps<Props<number>>();
利用 withDefaults 保持泛型完整性
如果泛型 Props 需要默认值,不能直接对泛型调用 withDefaults,而应先构造 props 类型,再包裹:
立即学习“前端免费学习笔记(深入)”;
- 先用
defineProps获取泛型 props 类型(不带默认值) - 再用
withDefaults传入相同泛型接口,并提供默认值对象 - 确保默认值类型与泛型约束一致(例如
T不能是never或未约束的unknown)
示例:
interface ListProps<T> {
data: T[];
emptyText?: string;
}
const props = withDefaults(
defineProps<ListProps<string>>(),
{
emptyText: 'No data'
}
);
避免常见错误:不要在 defineProps 调用中直接写泛型函数
以下写法 ❌ 会报错或失去类型推导:
-
defineProps<T>()—— TypeScript 不允许泛型调用签名无上下文 -
defineProps<{ item: T }>()——T未声明,编译失败 - 试图在 SFC 内部用
type Props<T> = ...然后defineProps<Props<T>>()——T作用域不存在
正确做法是把泛型约束移到接口或类型别名定义处,并在实例化时填入具体类型。
进阶:组合式函数中复用泛型 Props 类型
当多个组件共享同一套泛型逻辑时,可将泛型 Props 提取为独立类型文件:
- 新建
types/list-props.ts,导出export type ListProps<T> = { data: T[]; ... } - 各组件中直接导入并使用
defineProps<ListProps<User>>() - 配合
defineEmits<{ 'update': [value: T] }>()可实现完整泛型事件契约
这样既提升复用性,也便于集中管理类型契约和文档注释。


















