TypeScript + Vue3 实战
Vue 3 原生支持 TypeScript:<script setup> 配合 defineProps/defineEmits 的类型参数能让组件接口完全类型化,模板中的拼写错误也会被 vue-tsc 提前发现。
创建项目
官方脚手架提供 Vue3 + TS 模板,创建时勾选 TypeScript 即可:
npm create vue@latest my-vue-app
cd my-vue-app && npm install && npm run dev
npm run type-check # 用 vue-tsc 检查模板与脚本
vue-tsc 会同时检查 .vue 里的模板与脚本,"模板也安全"正源于此。
script setup 基础写法
单文件组件开启 lang="ts" 就能使用完整类型能力:
<script setup lang="ts">
import { ref } from "vue";
const count = ref(0); // 推断为 Ref<number>
const message = ref("你好"); // 推断为 Ref<string>
function inc(): void {
count.value++; // 脚本中需写 .value
}
</script>
<template>
<button @click="inc">点击 {{ count }}</button>
<p>{{ message }}</p>
</template>
模板中 ref 自动解包,直接写 count 即可。
props 的类型声明
defineProps 用类型参数声明入参,withDefaults 提供默认值:
<script setup lang="ts">
interface Props {
title: string;
level?: number;
tags?: string[];
}
const props = withDefaults(defineProps<Props>(), { level: 1, tags: () => [] });
</script>
渲染时模板里直接写 title 与 level,脚本中访问 props.title,均有类型提示。
emits 与事件负载类型
defineEmits 用"调用签名"声明事件名与负载,父组件写错参数会立刻报错:
<script setup lang="ts">
const emit = defineEmits<{
(e: "save", payload: { id: number; text: string }): void;
(e: "close"): void;
}>();
function doSave(): void {
emit("save", { id: 1, text: "demo" }); // 负载必须符合类型
}
</script>
<template>
<button @click="doSave">保存</button>
<button @click="emit('close')">关闭</button>
</template>
用类型写业务模块
把纯逻辑抽成 .ts,让组件只负责视图:
export interface Task {
id: number;
done: boolean;
}
export function toggle(t: Task): Task {
return { ...t, done: !t.done };
}
组件中再用 ref<Task[]> 保存列表、勾选时调用 toggle 更新状态即可。
小结
Vue3 + TS 的核心是让"组件契约"可检查:defineProps 定入参、defineEmits 定事件、ref 定状态、vue-tsc 校验模板,把类型写进组件边界,项目越大收益越明显。