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 校验模板,把类型写进组件边界,项目越大收益越明显。

笔记加载中…