vue 3 + ts 中应使用 interface 定义 props 结构并配合 withdefaults 设置默认值,避免 proptype 强转;emits 同样需用接口或元组声明事件签名,实现双向类型安全。

在 Vue 3 + TypeScript 项目中,为 defineProps 定义强类型接口与默认值,核心是「接口定义结构」+「withDefaults 注入默认值」,而非混用运行时校验对象。这种方式让类型推导完整、IDE 补全精准、重构安全,也符合 Vue 官方推荐的 S 级 TS 体验。
用 interface 明确描述 props 结构
先写一个纯 TypeScript 接口,清晰声明每个 prop 的名称、类型、可选性(?)和嵌套规则。避免使用 any 或模糊联合类型,优先用字面量联合(如 'primary' | 'secondary')或泛型约束复杂值。
- 必填字段不加
?,TS 编译时会强制传入 - 可选字段加
?,后续可配合默认值使用 - 数组、函数、对象等复杂类型直接写明,例如
rules?: Array boolean }> - 嵌套对象建议单独抽离成接口,提升复用性和可读性
用 withDefaults 绑定默认值
withDefaults 是编译器宏,无需导入,直接包裹 defineProps<t>()</t> 即可。它保留完整的类型信息,且支持对复杂类型(如对象、数组)用函数返回默认值,防止引用污染。
- 字符串、数字、布尔等基础类型可直接写值:
label: '请选择' - 数组、对象必须用箭头函数:
options: () => [{ label: '全部', value: '' }] - 默认值字段必须是接口中已声明的可选属性,否则 TS 会报错
- IDE 悬停能同时看到类型定义和默认值,开发时一目了然
避免类型退化:别用 PropType 强转代替接口
像 type: String as PropType 这类写法虽能工作,但绕过了接口的语义表达,导致类型无法被自动推导、无法参与泛型推导、也不利于文档生成。
- 所有字面量枚举场景,优先走接口字段定义:
size?: 'large' | 'small' | 'mini' - 需要动态适配的(如根据
mode决定value类型),改用泛型 + 条件类型封装,而不是运行时断言 -
emits同理:用接口或元组定义事件签名,而不是只写字符串数组
配合 defineEmits 实现双向类型校验
Props 有类型,Emit 也要有。用 defineEmits 配合接口或元组,能让组件调用方传参时就被 TS 检查。
- 单事件简单场景用元组:
defineEmits() - 多事件混合场景用接口:
defineEmits() - 这样在子组件里写
emit('change', 123),TS 会立刻提示类型错误











