Skip to content

Vue3 + TypeScript 与 Pinia(一) ​

本页对应 Vue3 + TypeScript 与 Pinia,共 7 段视频,约 1 小时 1 分。

学习目标 ​

  • 能够使用 npm create vue@latest -- --template vue-ts 创建 Vue3+TS 项目,并说明 typescript、vue-tsc、Vue Language Tools(Volar)、TypeScript Vue Plugin 在开发阶段与打包阶段各自的作用。
  • 能够为 ref 区分简单类型与复杂类型两种场景完成类型标注:简单数据依赖类型推导,复杂数据使用泛型参数,并验证赋值校验与模板中的属性提示均生效。
  • 能够为 reactive 对象选择类型推导或类型别名显式注解(含可选属性),并说明显式注解对初始参数和后续属性赋值的双向约束。
  • 能够说明 computed 无需手动标注类型的原因,并基于已标注的响应式数据推导出过滤后计算属性的元素类型,在模板遍历时获得属性提示。
  • 能够为事件处理函数参数标注 Event 类型,并通过 as HTMLInputElement / as HTMLButtonElement 断言获得精确的 DOM 类型提示。
  • 能够为模板引用标注 Ref<HTMLxxxElement | null> 类型,并分别用可选链、条件判断、非空断言三种方式处理空值,说明非空断言在运行时无实际判空逻辑的风险。

知识点 ​

创建 Vue3 + TS 开发环境 ​

为什么需要:基础阶段用的是纯 TS 环境,进入 Vue3 项目后要同时支持 .vue 文件和 TS 类型检查,所以要用 Vite 的 Vue + TS 模板。怎么想:创建命令与纯 TS 模板相似,只把模板参数改成 vue-ts。怎么做:

bash
npm create vite@latest vue-ts-pro -- --template vue-ts
cd vue-ts-pro
npm install
npm run dev

打开 package.json,重点看 devDependencies:typescript 负责 TS 编译能力,vue-tsc 是 Vue 官方基于 TypeScript 封装的类型检查工具。scripts.build 通常是先执行 vue-tsc,再执行 vite build。入口 main.ts 仍然完成 App 组件渲染并挂载到 #app;App.vue 的 <script> 要写 lang="ts",否则不能正常写 TS。

开发阶段有两个关键工具:Volar 对 .vue 文件提供实时类型错误反馈;TypeScript Vue Plugin 支持在 .ts 中 import .vue 文件。调试时禁用后者,main.ts 里导入 App.vue 会报找不到模块;重新启用后恢复正常。打包阶段由 vue-tsc 做最终类型检查:在 App.vue 写一个类型错误,开发时 Volar 会提示;即使漏掉,执行 npm run build 也会报错。这样设计是为了开发时把类型提示交给编辑器,保证 Vite 运行速度;打包时再兜底校验。

易错点:命令里的模板名是 vue-ts;lang="ts" 不能丢;不要以为开发时没红波浪线就一定没问题,打包还会检查。

为 ref 标注类型 ​

为什么需要:给 ref 标注类型,本质是约束 ref 对象的 value 属性:赋值时校验,使用时提示。怎么想:简单类型用类型推导;复杂类型用泛型。怎么做:

ts
import { ref } from 'vue'

const count = ref(100) // 推导为 Ref<number>
count.value = 200
// count.value = '100' // 报错

const id = ref<string | number>('2028')
id.value = 2030

复杂对象数组可写:

ts
type Item = { id: string; name: string; price: number }
const list = ref<Item[]>([])
list.value = [{ id: '1001', name: '男士鞋子', price: 200 }]

在模板中 v-for 遍历时,输入 item. 会自动提示 id/name/price。易错点:泛型不仅约束 value,也约束 ref 的初始值;简单类型不要过度手写泛型。

为 reactive 标注类型 ​

为什么需要:修改响应式对象属性时要类型约束,访问属性时要代码提示。怎么想:能根据默认对象推导出所需类型就用推导;推导不出,比如有可选字段,就用类型别名显式标注。怎么做:

ts
import { reactive } from 'vue'

type Form = { username: string; password: string; age?: number }
const form = reactive<Form>({ username: 'tom', password: '123' })
form.username = 'jerry'
// form.username = 100 // 报错

易错点:显式标注后,reactive 的初始值也必须满足类型;可选属性写 ?,但一旦传入仍要类型正确。

为 computed 标注类型 ​

为什么需要:计算属性通常由已知响应式数据计算得到,类型可由依赖自动推导;多数计算属性只读,主要收益是代码提示。怎么想:优先依赖自动推导,不手动标注。怎么做:

ts
const count = ref(100)
const doubleCount = computed(() => count.value * 2)
// doubleCount.value 推导为 number

const list = ref<Item[]>([])
const filterList = computed(() => list.value.filter(i => i.price > 500))

模板里遍历 filterList 时仍能提示 id/name/price。易错点:不要脱离依赖数据凭空标注;计算属性一般不做赋值。

为事件处理函数标注类型 ​

为什么需要:原生事件处理函数的参数默认容易落到 any,访问 e 没有提示。怎么想:先给事件对象标注 Event,需要更精确的 DOM 类型时再断言 target。怎么做:

ts
function inputChange(e: Event) {
  const target = e.target as HTMLInputElement
  console.log(target.value)
}

按钮点击同理:e.target as HTMLButtonElement。易错点:不断言时 e.target 类型较宽,直接访问 value 会报错;断言要基于真实 DOM 元素。

为模板引用标注类型 ​

为什么需要:模板引用拿到真实 DOM 后,希望访问其属性和方法时有提示。怎么想:ref 泛型写“具体 DOM 类型 | null”,因为挂载前是 null。怎么做:

ts
const inputRef = ref<HTMLInputElement | null>(null)
onMounted(() => {
  inputRef.value?.focus()
})

模板:<input ref="inputRef" />。易错点:必须处理 null,所以常用可选链;onMounted 之后才访问。

非空值处理 ​

为什么需要:对象属性可能为 null 或 undefined,访问其属性会报类型错误。怎么想:有三种方案:可选链、条件判断、非空断言。怎么做:

ts
inputRef.value?.focus() // 可选链
if (inputRef.value) inputRef.value.focus() // 条件判断
inputRef.value!.focus() // 非空断言

易错点:非空断言只是骗过 TS,没有运行时判断;无法确保非空时不要用 !,否则真实运行可能报错。

创建 Vue + TS 项目 ​

为什么需要:基础阶段的纯 TS 环境只能练习 TS 语法,不能直接开发 Vue 单文件组件;Vue + TS 项目需要模板、类型检查工具和编辑器支持一起配合。
怎么想:Vite 已支持快速创建 Vue + TS 模板,核心是把模板参数改成 vue-ts。
怎么做:在目标目录打开命令行,执行:

bash
npm create vite@latest vue-ts-pro -- --template vue-ts

说明:npm create vite@latest 表示基于最新版 Vite 创建项目;vue-ts-pro 是项目名,可自定义;-- --template vue-ts 表示选择 Vue + TS 模板。创建完成后进入项目,安装依赖并启动:

bash
npm install
npm run dev

看到 Vite 本地地址和 Vue 计数器页面,说明环境跑通。
易错点:项目名不要与已有目录冲突;模板参数不要漏写;启动前先安装依赖。

认识 Vue + TS 相关工具 ​

为什么需要:.vue 文件不是普通 .ts 文件,开发时的类型提示、.vue 导入、打包前类型检查需要不同工具配合。
怎么想:开发阶段优先保证编辑器和 Vite 速度,打包阶段再做兜底类型检查。
怎么做:在 package.json 的 devDependencies 中重点看 typescript、vue-tsc。开发阶段常见两个工具:Volar 对 .vue 文件提供实时类型错误反馈;TypeScript Vue Plugin 支持在 .ts 中 import .vue 文件。打包阶段 vue-tsc 做最终类型检查,npm run build 中通常先执行 vue-tsc,再执行 vite build。
易错点:不要以为开发时没报错就万事大吉;vue-tsc 会在打包前兜底检查,遗漏的类型错误仍可能让构建失败。

为 ref 标注类型 ​

为什么需要:给 ref 标注类型,本质是约束 ref 对象 value 属性的类型;赋值时能校验,使用时能提示。
怎么想:简单类型交给类型推导;复杂类型用泛型。
怎么做:

ts
import { ref } from 'vue'

const count = ref(100) // 推导为 Ref<number>
const value = ref<string | number>('2028')
value.value = 2030

复杂对象数组可先定义类型:

ts
type Item = { id: string; name: string; price: number }
const list = ref<Item[]>([])
list.value = [{ id: '1001', name: '男士鞋子', price: 200 }]

模板中遍历时,item. 会自动联想 id/name/price。
易错点:使用泛型后,初始值也受约束;例如 ref<string | number>('2028') 中初始值必须满足联合类型。

为 reactive 标注类型 ​

为什么需要:reactive 对象修改属性时要有类型约束,访问属性时要有代码提示。
怎么想:默认对象能推导出所需类型时直接用推导;推导不出或需要可选字段时,用类型别名显式注解。
怎么做:

ts
import { reactive } from 'vue'

type Form = { userName: string; password: string; gender?: string }
const form = reactive<Form>({ userName: '张三', password: '123456' })
form.userName = '李四'

易错点:显式注解后,传入 reactive 的初始对象也要满足类型;可选字段可省略,但写了就必须类型正确。

为计算属性标注类型 ​

为什么需要:计算属性通常由已知响应式数据计算得到,类型可由依赖自动推导;使用时需要提示。
怎么想:计算属性多数只读,不需要手动赋值,因此优先依赖自动推导。
怎么做:

ts
const count = ref(100)
const doubleCount = computed(() => count.value * 2)
const list = ref<Item[]>([])
const filterList = computed(() => list.value.filter(item => item.price > 500))

doubleCount.value 会提示 number 的方法;filterList 中每项仍能提示 id/name/price。
易错点:不要为计算属性硬写不必要类型;先确认依赖数据类型是否已明确。

为事件处理函数标注类型 ​

为什么需要:原生 DOM 事件回调参数默认可能是 any,访问 event.target 时提示很少。
怎么想:先给事件对象标 Event,需要具体 DOM 属性时再断言 target 的具体元素类型。
怎么做:

ts
function inputChange(e: Event) {
  const target = e.target as HTMLInputElement
  console.log(target.value)
}

按钮点击可断言为 HTMLButtonElement。
易错点:e.target 默认较宽泛,不断言就访问 value 等属性容易报错;断言要基于真实元素类型。

为模板引用标注类型 ​

为什么需要:通过 ref 获取真实 DOM 后,需要明确 value 中存放的元素类型,才能获得准确提示。
怎么想:用“具体元素类型 | null”作为泛型,因为组件渲染完成前 value 可能是 null。
怎么做:

ts
const inputRef = ref<HTMLInputElement | null>(null)

onMounted(() => {
  inputRef.value?.focus()
})

模板中绑定 ref="inputRef"。
易错点:访问时加可选链 ?.,否则 value 可能为 null 导致类型错误。

对象非空值处理 ​

为什么需要:对象属性可能为 null 或 undefined,直接访问其属性或方法会报类型错误。
怎么想:常用三种方案:可选链、逻辑判断、非空断言。
怎么做:

ts
inputRef.value?.focus()

if (inputRef.value) {
  inputRef.value.focus()
}

inputRef.value!.focus()

易错点:非空断言 ! 只是让 TS 通过校验,没有真实运行时判断;若实际仍为空,浏览器运行时会出错。可选链和 if 判断有真实运行时保护,更稳妥。

先看整体:开发阶段与打包阶段的分工 ​

这一节的核心不是某个 API,而是理解 Vue + TypeScript 项目里“谁来检查 .vue 文件类型”。转写稿里先创建了 vue-ts-pro 项目,打开 package.json 的 devDependencies,能看到 typescript 和 vue-tsc;随后运行项目,并在 App.vue 的 <script> 上保留 lang="ts"。这说明:.vue 文件要写 TS,必须开启 lang="ts",否则 TS 支持不会生效。

整体分工可以记成一句话:开发阶段由 IDE 相关工具做实时提示,打包阶段由 vue-tsc 做最终兜底。这样设计的原因是:实时类型检查如果全交给打包工具,会影响 Vite 的开发运行速度;而只在 IDE 里提示又可能被开发者忽略,所以打包前再统一校验一次。

Vue 与 TypeScript 在开发阶段和打包阶段的工具分工图

Volar:开发时的实时类型反馈 ​

为什么需要:在 .vue 文件里写 TS 时,如果编辑器不能实时提示类型错误,只能等打包才发现问题,反馈太慢。
怎么想:把“实时反馈”交给 VS Code 的语言服务插件,让错误在写代码时就以红色波浪线出现。转写稿中在 App.vue 里写了一个 count.value 的错误赋值:原本是 number,却赋了字符串,Volar 立刻提示“不能将类型 string 分配给类型 number”。
怎么做:在 VS Code 扩展中安装并启用 Volar;打开 .vue 文件,在 <script lang="ts"> 中写 TS 代码,观察类型提示。示意代码:

ts
const count = ref(100)
count.value = '100' // Volar 实时提示:不能将 string 分配给 number

易错点:一是忘记写 lang="ts",导致 TS 语法支持没开启;二是扩展被禁用或工作区未启用,实时提示消失;三是把 Volar 的红色提示误认为项目一定跑不起来——开发服务器可能仍在运行,真正阻断打包的是后面的 vue-tsc。

TypeScript Vue Plugin:让 TS 能 import *.vue ​

为什么需要:TypeScript 本身不认识 .vue 文件。入口 main.ts 里通常会写 import App from './App.vue',如果没有相应插件支持,TS 会认为找不到这个模块。
怎么想:Volar 负责 .vue 内部的类型反馈,TypeScript Vue Plugin 负责让 TS 语境识别 .vue 模块导入,两者配合使用。
怎么做:在 VS Code 中安装并启用 TypeScript Vue Plugin。转写稿的调试过程很直接:先禁用该插件,回到 main.ts,import App from './App.vue' 立刻报错;重新启用后,这条导入恢复正常。示意代码:

ts
import App from './App.vue'

易错点:只启用 Volar 而忽略 TypeScript Vue Plugin,可能出现 .vue 导入报“找不到模块”;排查时先看扩展是否启用,再看是否在正确的工作区打开项目。

vue-tsc:打包前的兜底类型检查 ​

为什么需要:开发时可能漏看 Volar 的提示,或者错误被暂时忽略。打包阶段需要一次统一、最终的类型检查,防止错误代码进入构建产物。
怎么想:vue-tsc 是 Vue 官方基于 tsc 做的一层封装,专门处理 Vue 项目中的类型检查。它不负责开发服务器的实时反馈,而是放在 build 脚本里做“兜底”。
怎么做:在 package.json 中确认 vue-tsc 依赖,并观察构建脚本,通常是先执行 vue-tsc,再执行 vite build:

json
{
  "scripts": {
    "build": "vue-tsc && vite build"
  },
  "devDependencies": {
    "typescript": "...",
    "vue-tsc": "..."
  }
}

转写稿中故意在 App.vue 留下 count.value 的类型错误,然后执行 npm run build,vue-tsc 在打包前报错,并指出错误发生在 src/App.vue 的 count.value 处。
易错点:不要把 vue-tsc 当成开发服务器实时检查工具;构建脚本顺序不要写反,先 vue-tsc 后 vite build,类型检查失败就不应继续打包;转写稿口述里的“view 杠 TSC”“wa build”按实际包名和脚本应写为 vue-tsc、vite build。

从创建到验证:把三个工具串起来 ​

创建项目可使用与转写稿一致的 Vite 模板命令,项目名可自定义:

bash
npm create vite@latest vue-ts-pro -- --template vue-ts

随后安装依赖并启动:

bash
npm install
npm run dev

验证顺序建议是:打开 App.vue 确认 <script lang="ts">;写一个错误赋值看 Volar 是否实时提示;在 main.ts 看 import App from './App.vue' 是否正常;最后执行 npm run build,看 vue-tsc 是否在打包前报出同一类错误。这样就能把“开发实时反馈”和“打包兜底校验”串成完整链路。

课件图示 ​

课件第17页:如何为事件处理函数标注类型 事件处理函数的类型标注主要做俩个事 1. 给事件对象

课件第5页:场景和好处 为ref标注类型之后,既可以在给ref对象的value赋值时校验数据

课件第9页:场景和好处 为reactive标注类型之后,既可以在响应式对象在修改属性值的时候

课件第54页:总结 总结 1. 使用Vue + TS开发比较重要的是哪些类型的定义? 响应式数

课件第3页:和.vue文件TS环境相关的工具职责说明 开发阶段 1. Volar工具对.vu

课件第10页:如何标注类型 reactive函数和TS的配合通常分为俩种情况,类型推导和显式标

课件第14页:更复杂的例子 需求:给ref函数标注类型,接收后端返回的对象列表,然后使用计算属

课件第20页:为什么给模板引用标注类型 给模版引用标注类型,本质上是给ref对象的value属

代码示例 ​

① 创建 Vue3 + TS 开发环境(终端命令)

bash
# 基于最新版 vite 创建项目,项目名 vue-ts-pro,模板选 vue-ts
npm create vite@latest vue-ts-pro -- --template vue-ts

cd vue-ts-pro
npm install     # 安装所有依赖(含 typescript、vue-tsc)
npm run dev     # 启动开发服务器,看到 Vue + 计数器页面即成功

要点解释 / 易错点:

  • -- --template vue-ts 中间那个 -- 不能丢,它的作用是把参数透传给 vite,丢了就会变成交互式询问模板。
  • vue-ts-pro 是项目名,可自定义,但必须小写、不能有中文。
  • 创建后先看 package.json 的 devDependencies:多了 typescript(编译器)和 vue-tsc(Vue 官方的类型检查工具,打包时兜底)。
  • build 脚本通常是 vue-tsc && vite build:打包前先做一次全量类型检查,开发阶段的类型提示交给 IDE(Volar + TypeScript Vue Plugin)保证 vite 运行速度。

② ref 标注类型 + computed 自动推导(App.vue)

vue
<!-- App.vue -->
<script setup lang="ts">
import { ref, computed } from 'vue'

type Item = {
  id: string
  name: string
  price: number
}

// 复杂类型:用泛型指定,同时约束了 value 的赋值和初始值
const list = ref<Item[]>([
  { id: '1001', name: '男士鞋子', price: 200 },
  { id: '1002', name: '女士背包', price: 600 }
])

// 简单类型:直接交给类型推导,不写任何 TS 代码
const count = ref(100)

// computed 依赖已知响应式数据,类型自动推导,无需手动标注
const filterList = computed(() =>
  list.value.filter((item) => item.price > 500)
)
</script>

<template>
  <ul>
    <li v-for="item in filterList" :key="item.id">
      {{ item.id }} — {{ item.name }} — {{ item.price }}
    </li>
  </ul>
  <p>{{ count.toFixed(2) }}</p>
</template>

要点解释 / 易错点:

  • <script setup lang="ts"> 里的 lang="ts" 不能丢,它是开启 TS 支持的语法,丢了就写不了 TS 代码。
  • ref 标注类型的本质是给 value 加类型约束:list.value = [{...}] 时少写 price 会立刻报「缺少属性 price」。
  • ref<Item[]> 的泛型同样约束了初始值,初始值里的对象也必须满足 Item。
  • 简单类型(number / string / boolean)不要画蛇添足写 ref<number>(100),推导即可;复杂结构才用泛型。
  • filterList 是 ComputedRef<Item[]>,模板里 item. 会自动联想出 id / name / price,这就是 computed 不用手写类型的原因。

③ reactive 显式标注 + 事件处理函数 + 模板引用 + 非空值处理(App.vue)

vue
<!-- App.vue -->
<script setup lang="ts">
import { ref, reactive, onMounted } from 'vue'

// isAgree 是可选属性,reactive 推导不出来 → 用类型别名显式标注
type Form = {
  userName: string
  password: string
  isAgree?: boolean
}

const form = reactive<Form>({
  userName: 'zhangsan',
  password: '123456'
})

// 模板引用:具体 DOM 类型 联合 null,null 是为了兼容初始值
const inputRef = ref<HTMLInputElement | null>(null)
const btnRef = ref<HTMLButtonElement | null>(null)

// 事件对象默认推导为 any,手动标注 Event 才有提示
const inputChange = (e: Event) => {
  const target = e.target as HTMLInputElement // 断言成具体 DOM 类型才能拿到 value
  console.log(target.value)
}

const btnClick = (e: MouseEvent) => {
  console.log((e.target as HTMLButtonElement).innerText)
}

onMounted(() => {
  inputRef.value?.focus()      // 方案一:可选链,前面是 null 就停止运算
  if (btnRef.value) {          // 方案二:条件判断,有值才访问
    btnRef.value.innerText = '已点击'
  }
  // inputRef.value!.focus()   // 方案三:非空断言,必须确保不是 null,慎用
})
</script>

<template>
  <input ref="inputRef" @change="inputChange" />
  <button ref="btnRef" @click="btnClick">按钮</button>
  <input v-model="form.userName" />
  <p>{{ form.userName }}</p>
</template>

要点解释 / 易错点:

  • reactive 能推导出就用推导,只有像 isAgree?: boolean 这种可选字段才需要 type 别名 + reactive<Form> 显式标注;标注后初始值和后续赋值都会校验。
  • 事件处理函数的 e 不写类型会被推成 any,IDE 里没有提示;先标 Event,再用 e.target as HTMLInputElement 断言,才能点出 value。
  • 模板引用必须写 ref<HTMLInputElement | null>(null):少了 | null 会与初始值 null 冲突;ref 变量名要和模板上 ref="inputRef" 完全一致。
  • DOM 只能在 onMounted 之后访问,此时 inputRef.value 仍可能是 null,所以要用可选链 ?. 或 if 判断。
  • 非空断言 ! 只在 TS 编译期生效,运行时没有保护,一旦真的为 null 会直接报错,前两种方案更安全。

常见错误 ​

  1. 创建项目时模板没生效,装出来的是纯 TS 或默认模板 现象:执行 npm create vite@latest vue-ts-pro --template vue-ts 后没有 App.vue,或仍被交互式提问。 原因:--template 是传给 create-vite 的参数,npm 需要额外的 -- 才能透传。 修法:写成 npm create vite@latest vue-ts-pro -- --template vue-ts,创建后检查 package.json 的 devDependencies 中是否有 vue-tsc。

  2. .vue 文件里写 TS 语法直接报错,或类型完全不生效 现象:<script setup> 中写 ref<number>(0) 报语法错误;模板里点不出属性提示。 原因:漏写 lang="ts",SFC 仍按 JS 解析。 修法:<script setup lang="ts">;确认编辑器启用的是 Volar(Vue - Official)而非 Vetur。

  3. TS 文件里 import App from './App.vue' 报"找不到模块" 现象:入口 main.ts 里 import .vue 出现红色波浪线,但项目能跑。 原因:缺少 TypeScript Vue Plugin(或 Volar 插件被禁用、TS 服务未刷新)。 修法:安装并启用 Volar + TypeScript Vue Plugin,然后在命令面板执行 Restart TS Server;同时卸载/禁用 Vetur。

  4. 开发时一路绿灯,npm run build 才爆类型错误 现象:IDE 没提示,打包时 vue-tsc 报出若干类型错误并中断。 原因:开发阶段类型检查由 IDE 承担,Vite 只做转译不做类型校验;vue-tsc 在 build 前做兜底检查。 修法:把 npm run build 或单独的 vue-tsc --noEmit 纳入提交前/CI 检查,不要以"页面能跑"作为通过标准。

  5. ref 初始值给了 null 或空数组,后续赋值全报错 现象:const list = ref([]) 后 list.value = data 报"不能将 Item[] 分配给 never[]";ref(null) 后访问属性报错。 原因:初始值过弱,推导出的类型被收窄成 never[] / null。 修法:复杂类型一律显式传泛型:ref<Item[]>([])、ref<HTMLInputElement | null>(null)。

  6. reactive 对象整体重新赋值,响应式和类型双双出问题 现象:form = { ...newForm } 后页面不再更新,或提示不能赋值。 原因:reactive 返回的是代理引用,替换变量本身会丢掉响应式连接。 修法:用 Object.assign(form, newForm) 或逐字段赋值;确需整体替换时改用 ref + .value。

  7. 模板引用直接 xxxRef.value.focus() 报"对象可能为 null" 现象:onMounted 中访问模板引用提示可能为空。 原因:ref 的 value 在挂载前是 null,类型是联合类型。 修法:优先用可选链 xxxRef.value?.focus(),或先 if (xxxRef.value) 判断;非空断言 ! 只在能确定已挂载时使用。

  8. 滥用非空断言 ! 掩盖空值 现象:编译通过,运行时浏览器报 Cannot read properties of null。 原因:! 只在 TS 层消除告警,不产生任何运行时判断。 修法:能用 ?. 或 if 判断就不要用 !;! 仅限"逻辑上不可能为空"的场景,并加注释说明。

练习 ​

  1. 用一条命令创建名为 vue-ts-pro 的 Vue3 + TS 项目,并逐段解释命令各部分含义;创建完成后打开 package.json,指出与类型检查相关的两个依赖,以及 build 脚本中两条命令的执行顺序和各自职责。

  2. 定义商品类型 Item { id: string; name: string; price: number },用 ref 声明一个初始为空的商品列表并保证赋值时类型受约束;再声明一个"既可存 string 也可存 number"的 ref,并说明为什么第一种情况不能用类型推导、第二种必须用泛型。

  3. 用 reactive 定义用户表单,字段包含 userName、password 和可选的 gender。分别说明什么情况下用类型推导、什么情况下必须用类型别名显式注解,并各写出一段代码。

  4. 为 <input> 的 change 事件和 <button> 的 click 事件编写处理函数并标注类型,要求能在函数内访问 target.value 时获得精确提示;说明标注过程中两个必要步骤分别解决了什么问题。

  5. 用模板引用获取页面上的 <input> 元素并在挂载后使其聚焦。写出三种规避"可能为 null"的写法,并比较三者在运行时行为上的差异。

原视频对照 ​

#视频时长
1Vue3+TS环境创建10:49
2为ref标注类型09:54
3为reactive标注类型修复12:49
4为计算属性标注类型07:22
5为事件处理函数标注类型06:50
6为模版引用标注类型07:41
7对象的非空值处理06:24

小结 ​

  1. 环境创建的关键是 -- --template vue-ts,多一个 -- 决定模板是否生效。
  2. 三个工具分工明确:Volar 负责开发期实时类型反馈,TypeScript Vue Plugin 负责 TS 中导入 .vue,vue-tsc 负责打包期兜底校验。
  3. "IDE 报错"和"打包报错"不是同一件事,能跑起来不等于类型通过,CI 中必须有 vue-tsc 这一关。
  4. ref 的类型标注本质是约束 .value:简单类型靠推导,复杂类型靠泛型,初始值过弱时必须显式泛型。
  5. reactive 优先用类型推导;只有出现可选字段、推导不出目标类型时才用类型别名显式注解,且注解同时约束初始值和后续属性赋值。
  6. computed 依赖响应式数据自动推导,通常无需手写类型,收益主要体现在模板中的代码提示。
  7. 事件处理函数默认 e 为 any(或宽松类型),需两步:先标注事件类型拿到基础提示,再用断言收窄 target 拿到精确 DOM 提示。
  8. 模板引用用 具体元素类型 | null 的泛型声明;空值处理三种方案中,?. 和 if 有真实运行时保护,非空断言 ! 没有,须谨慎。