vue 3 + typescript 类型安全核心是全链路类型贯穿:props 用泛型接口+withdefaults、emits 用函数重载签名、响应式状态显式泛型标注、slots 用 slotstype 约束。

在 Vue 3 的 `<script setup lang="ts">` 中结合 TypeScript,核心是让类型信息贯穿 props、emits、响应式状态和事件处理全流程,而不是零散加注解。关键不在于“能不能写类型”,而在于“类型是否真正约束运行时行为并被 IDE 全链路识别”。</script>
Props 类型定义:两种方式,选对场景
推荐优先使用基于类型的声明(泛型接口),语义清晰、IDE 补全强、与 TS 生态自然融合:
- 直接内联类型:
const props = defineProps(); - 拆分为独立接口(适合复用或复杂结构):
interface ButtonProps { size: 'small' | 'medium' | 'large'; onClick?: () => void; }const props = defineProps<buttonprops>();</buttonprops> - 需要默认值?搭配
withDefaults:const props = withDefaults(defineProps<buttonprops>(), { size: 'medium' });</buttonprops>
避免混用“运行时声明”(如 defineProps({ title: String }))和泛型方式——二者不可共存,且运行时声明对联合类型、字面量类型支持弱。
Emits 类型签名:明确事件名与 payload 结构
用 defineEmits 声明一个函数重载签名,不是简单字符串数组:
- 正确写法(支持多事件、多参数、可选参数):
const emit = defineEmits (e: 'change', value: string): void;<br> (e: 'update:modelValue', value: number): void;<br> (e: 'error', code: number, msg: string): void;<br> (e: 'clear'): void;<br>>();
- 触发时自动校验:
emit('change', 'ok')合法;emit('change', 123)编译报错 - 父组件监听时,
@change回调参数类型自动推导为string,无需额外注解
响应式状态与计算属性:显式泛型优于隐式推导
复杂结构或初始值为 null/undefined 时,主动标注类型更安全:
-
const user = ref<user null>(null);</user>—— 避免后续访问user.value.name时报错 -
const items = ref<product>([]);</product>—— 明确数组元素类型,items.value.map(...)才有完整方法提示 -
const total = computed<number>(() => items.value.reduce((a, b) => a + b.price, 0));</number>—— 强制指定返回类型,防止推导为number | undefined
Slots 类型约束:让插槽内容也具备类型安全
当组件接收具名插槽或作用域插槽时,用 SlotsType 显式声明插槽函数签名:
import { SlotsType } from 'vue';const slots = defineSlots<slotstype default any header title: string item data:>>();</slotstype>- 这样父组件传入插槽时,
item插槽的data参数会被严格检查,IDE 也能提示可用字段











