主题
Vue3 + TypeScript 与 Pinia(二)
本页对应 Vue3 + TypeScript 与 Pinia,共 7 段视频,约 1 小时 19 分。
学习目标
- 能够使用
defineProps配合类型别名或接口为组件 props 标注类型,并通过withDefaults为可选 props 设置默认值。 - 能够使用
defineEmits约束组件自定义事件的名称与参数类型,并在触发事件时获得类型校验与提示。 - 能够说明
.d.ts类型声明文件的作用,区分自动生成、DefinitelyTyped 提供和 TS 内置类型声明文件的使用场景。 - 能够创建自定义
.d.ts文件共享 TS 类型,并为同目录下的 JS 模块补充类型声明。 - 能够依据后端接口文档定义嵌套数据类型,结合
axios.request泛型完成频道列表的数据请求、赋值与模板渲染。 - 能够依据接口文档定义文章列表类型,完成文章列表基础请求、响应式数据赋值、列表渲染及按字段条件渲染不同模板。
知识点
props 类型标注:先约束,再获得提示
为什么需要:组件对外暴露的 props 本质上是接口。没有类型时,父组件传错值只能在运行时暴露;有了类型,编辑器会提前报错,并在组件内部访问 props、父组件传参时给出提示。
怎么想:把“组件允许接收什么”先抽象成一个类型,再让 defineProps 读取这个类型。转写稿里的推导很直接:按钮组件要求 color 是必填字符串,size 是可选字符串;父组件不传 color 会报错,传 100 也会报“不能将 number 分配给 string”。
怎么做:两步。先定义类型,再用泛型传给 defineProps。
ts
type Props = {
color: string
size?: string
}
const props = defineProps<Props>()调试时先观察父组件:不传 color 是否报错;传 color="red" 是否恢复;再把 color 改成 100,确认类型校验生效。组件内部写 props. 时也应出现联想。
易错点:可选属性要加 ?;不要把 defineProps 当成普通函数随意调用;类型名和属性名区分清楚,string 不要写成 String。
props 默认值:可选参数才需要默认值
为什么需要:可选 props 用户可能不传。默认值让组件在“用户不传”时有稳定表现;用户传了,则以用户值为准。
怎么想:默认值只对可选参数有意义。转写稿中 size?: string 想默认成 middle,就用 withDefaults 包住 defineProps。
怎么做:
ts
const props = withDefaults(defineProps<Props>(), {
size: 'middle'
})验证方式:父组件传 size="big" 时显示 big;不传时显示 middle。
易错点:withDefaults 第二个参数的对象键必须和 props 属性同名;不要给必填参数设置“默认值”来掩盖父组件漏传的问题。
emits 类型标注:约束事件名和参数
为什么需要:自定义事件最怕两件事:事件名拼错、参数类型传错。给 emits 标注类型后,emit 的第一个参数会受事件名约束,第二个参数会受参数类型约束。
怎么想:转写稿把事件类型写成“调用签名”的固定格式:事件名在前,参数类型在后。一个组件可以有多个事件,并排写即可。
怎么做:
ts
type Emits = {
(e: 'getMessage', message: string): void
(e: 'getList', list: ListItem[]): void
}
const emit = defineEmits<Emits>()调试时输入 emit(',应提示 getMessage、getList;给 getMessage 传 100 应报错;事件名乱写也应报错。
易错点:事件名要和父组件绑定名一致;参数类型不要漏写;多个事件用分号或换行分隔清楚。
d.ts 类型声明文件:为 JS 模块补类型
为什么需要:很多第三方包发布的是 JS 文件,但使用时仍有提示,原因是包内带有 .d.ts,或由 @types/包名 补充。转写稿用 axios 举例:安装后能提示,是因为包内有类型声明文件;用 JS 编写且没有声明的库,会报“找不到模块的声明文件”。
怎么想:分三类。TS 编写的库,打包时通常自动生成 .d.ts;JS 编写的库,可尝试安装 @types/包名;数组、DOM 等标准 API 的类型来自 TS 内置声明文件,不需要安装。
怎么做:遇到“找不到声明文件”,先查该库是否支持 @types。安装后若编辑器未恢复,重启编辑器再验证提示。
易错点:.d.ts 只放类型信息,不能写可运行逻辑;它不参与编译成 JS,只做类型检查。
自定义 d.ts:共享类型与给 JS 文件补声明
为什么需要:业务里同一类型常被多个组件使用,集中到 types/*.d.ts 可复用;老项目里的 JS 文件也想获得提示,可用同名 .d.ts 补声明。
怎么想:共享类型用 export interface 导出,使用方用 import type 导入,明确“这是类型不是普通模块”。给 JS 补声明时,.d.ts 与 JS 文件同目录同名,用 declare 描述函数签名。
怎么做:
ts
// types/data.d.ts
export interface Goods {
id: string
name: string
}ts
import type { Goods } from './types/data'给 JS 函数补声明:
ts
declare function add(a: number, b: number): number
export default add易错点:共享类型文件路径要对;import type 不要写成普通导入;同名 .d.ts 要与 JS 文件对应。
综合案例:用接口文档驱动类型
为什么需要:Vue3 + TS 业务开发的核心不是“到处写类型”,而是让响应式数据、axios 返回值、模板渲染共享同一套接口类型,减少复制和错漏。
怎么想:转写稿的推导是:先看接口文档定义返回类型;ref 根据接口定义列表项类型;axios.request<T> 用泛型约束返回结构;赋值时类型自然匹配;模板渲染时享受提示。
怎么做:频道列表先声明 ref<ChannelItem[]>([]),请求时写 axios.request<ChannelRes>({...}),再把 res.data.data.channels 赋给列表。文章列表先固定 channelId: 0 完成基础渲染,后续再做频道点击联动。
ts
const channelList = ref<ChannelItem[]>([])
const res = await axios.request<ChannelRes>({ url, method: 'get' })
channelList.value = res.data.data.channels调试时点 res.data 看是否提示 data、message;再点 res.data.data.channels 看是否提示频道数组。模板里 v-for 时输入 item. 应提示 id、name。
易错点:类型要按接口文档定义,不要凭感觉写;ref 的泛型若是列表要加 [];请求参数 get 用 params;先固定 ID 跑通基础列表,再做联动,避免一开始把问题混在一起。
为什么要单独创建 Vue + TS 环境
为什么需要:基础阶段我们可能只创建过纯 TS 环境,但真正开发 Vue 项目时,代码里会出现 .vue 单文件组件、<script setup lang="ts">、组件 props、emits 等写法。纯 TS 环境不能直接承担 Vue + TS 的开发任务,所以需要快速创建一个支持 Vue 与 TS 的项目骨架。
怎么想:转写稿里明确说,Vite 除了支持基础阶段的纯 TS 环境,还支持 Vue + TS 开发环境的快速创建。也就是说,我们不需要手动从零配置 Vue 插件、TS 配置和构建脚本,而是直接用 Vite 官方脚手架选择 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 的开发模板。
易错点:不要把 -- --template vue-ts 写成普通参数,-- 后面的内容才是传给 Vite 脚手架的参数;如果模板参数没有正确传入,生成的项目可能不是 Vue + TS 模板。项目名建议使用小写、短横线形式,如笔记里的 vue-ts-pro,避免目录名冲突或特殊字符带来的麻烦。
创建后的验证思路
为什么需要:命令执行成功不等于项目能跑起来。创建环境后还要确认依赖、脚本和页面入口是否正常,否则后面写组件时会把环境问题误判成代码问题。
怎么想:转写稿在综合案例环境准备中强调:拉下别人的项目后,不要着急按以前的习惯直接执行命令,而要打开 package.json 看 scripts 里到底叫什么。这个思路同样适用于刚创建的项目:先看脚本名,再运行。
怎么做:进入项目目录后安装依赖,并按照 package.json 中的脚本启动开发服务器。常见验证流程如下:
bash
cd vue-ts-pro
npm install
npm run dev运行后终端会给出本地访问地址,打开浏览器访问该地址。转写稿中还提到切换到移动模拟器,如果能看到页面静态结构,就证明项目已经成功跑起来。这个“看到页面”的验证动作,比只看命令是否结束更可靠。
易错点:不要想当然地认为启动命令一定叫某个名字。转写稿明确提醒,scripts 里的命令名是可以自定义的,所以要打开 package.json 确认;看到链接地址后要打开对应页面验证,而不是只看终端输出。
命令中的关键细节
为什么需要:理解命令细节,才能在小问题出现时自己排查,而不是重复复制命令。
怎么想:npm create vite@latest 的重点是“用最新版 Vite 创建项目”;vue-ts-pro 的重点是“项目目录名”;--template vue-ts 的重点是“模板选择”。三者分别对应工具、目录和模板。
怎么做:执行时保持参数顺序清晰。创建完成后,可以观察项目根目录下的 package.json。转写稿在综合案例中查看包文件时,会关注核心依赖和开发依赖,例如 Vue、Vite、TypeScript 以及支持 TS 开发环境的类型检查工具。对于本节创建的环境,核心确认点是:这是 Vue + TS 模板,而不是纯 JS 或纯 TS 模板。
易错点:一是漏掉第二个 --,导致模板参数没有传给脚手架;二是项目名与已有目录重名;三是创建后不安装依赖就直接启动;四是不看 scripts 就凭记忆执行命令。转写稿中口述的“vi 杠 TSC”应理解为与 Vue TS 类型检查相关的工具,实际以项目 package.json 为准,不要凭口述拼写去改配置。
与后续综合案例环境准备的衔接
为什么需要:本节创建的是开发环境,后面综合案例还要在这个环境基础上加入请求库、组件库和静态模板。先把环境创建正确,后面的类型标注、接口请求和列表渲染才有稳定基础。
怎么想:转写稿的综合案例环境准备中,项目是基于 Vite 创建出来的 Vue3 + TS 开发环境,然后添加 axios 请求库、移动端组件库以及静态结构模板。也就是说,创建环境不是孤立步骤,而是后续所有 Vue + TS 练习的起点。
怎么做:先按本节命令创建 vue-ts-pro,确认能启动;再进入业务开发。后续查看项目时,先看 package.json 的依赖与 scripts,再运行对应命令。转写稿中“装依赖—看 scripts—执行 npm run dev—打开地址—切换移动模拟器—看到静态结构”的流程,可以作为环境验证的参考。
易错点:不要把后续案例里的 axios、组件库等依赖误认为本节创建命令自动包含的内容;本节只负责用 Vite 创建 Vue + TS 模板。依赖是否需要添加,要以具体项目和 package.json 为准。
本节要解决的问题
这一节笔记聚焦“和 .vue 文件相关的工具”。核心不是讲某个 Vue 语法,而是讲清楚:为什么开发时类型提示很顺、打包时还能再做一次类型检查。转写稿里反复出现的 props 必填校验、emits 参数校验、d.ts 类型声明,本质上都依赖同一套工具链:开发阶段由 IDE 负责实时反馈,打包阶段由 vue-tsc 做兜底。笔记给出的结论是:开发阶段的类型提示交给 IDE,保证 Vite 的运行速度;打包阶段做“兜底类型校验”,确保类型无误。

开发阶段:Volar 负责实时类型错误反馈
为什么需要:.vue 文件不是普通 .ts 文件,模板、script setup、props、emits 混在一起。如果 IDE 不能理解 .vue,开发时就没有实时红线和补全,只能等打包才发现错误。转写稿中给 button 组件定义 props:color 必填且为 string,size 可选;父组件不传 color 会报错,传 100 这种 number 也会提示“不能将 number 分配给 string”。这类即时反馈就是开发阶段工具的价值。
怎么想:开发阶段追求“快”和“即时”。Vite 负责快速启动和热更新,类型检查不应阻塞每一次运行;把实时类型提示交给 IDE 的 Volar,开发者写错时马上看到红线,改完马上消失。
怎么做:在 IDE 中启用 Volar,让 .vue 获得类型提示、补全和错误反馈。典型示例可写成:
ts
type Props = {
color: string
size?: string
}
const props = defineProps<Props>()父组件若传 color=100,Volar 应在编辑器中报类型错误。
易错点:不要把 IDE 报红当成“运行时一定报错”,也不要把 IDE 没报红当成“打包一定通过”。Volar 是开发期辅助,最终仍需打包检查;另外要确认当前编辑器确实启用了对应 Vue 语言支持,否则会出现“代码没错但没提示”的假象。
开发阶段:TypeScript Vue Plugin 支持在 TS 中导入 .vue
为什么需要:在 .ts 文件里写 import Button from './Button.vue' 时,TypeScript 默认不认识 .vue 模块。没有插件支持,编辑器会提示找不到模块声明,组件类型也拿不到。
怎么想:Volar 让 .vue 内部有类型能力,TypeScript Vue Plugin 则补上“从 TS 看 .vue”这一环。两者配合,.vue 既能被编辑,也能被 TS 正确导入。
怎么做:按笔记配置 TypeScript Vue Plugin,使 TS 中的 import *.vue 获得类型支持。转写稿中 d.ts 部分也说明了类型声明文件的作用:为 JS 模块提供类型支持,从而获得提示;但 .vue 导入应由对应工具/插件处理,而不是简单写成宽泛 any。
易错点:只装 Volar 但 TS 中导入 .vue 仍报“找不到模块”,要检查插件是否生效;也不要用过宽的模块声明把 .vue 全声明成 any,否则类型检查会被削弱,props、emits 的约束就形同虚设。
打包阶段:vue-tsc 做最终类型检查
为什么需要:开发阶段依赖 IDE,开发者可能关闭编辑器、CI 环境也可能没有 IDE;而且 Vite 为了速度不会在每次运行时做完整类型检查。因此打包阶段需要 vue-tsc 做最终校验,防止类型错误进入产物。
怎么想:把“实时反馈”和“最终验收”分开。开发时 Volar 快速反馈;打包时 vue-tsc 统一检查 .vue 与 TS 类型。转写稿里 props 传错 number、emits 事件名写错或参数类型不匹配,在开发时是红线,在打包时也应被 vue-tsc 拦住。
怎么做:在项目的打包/类型检查脚本中运行 vue-tsc,再执行构建。若项目脚本里写的是 vue-tsc,不要误读成 vi-tsc;转写稿中“vi 杠 TSC”应纠正为 vue-tsc。运行时先看 package.json 的 scripts,按项目实际命令执行。
易错点:把 vue-tsc 当成运行时依赖或浏览器工具;只跑 vite build 不跑类型检查;看到 IDE 没报错就跳过打包校验。正确理解是:Volar 负责开发体验,vue-tsc 负责兜底。
工具协作与调试过程
转写稿的调试现象可以串起来理解:定义 props 后,父组件少传必填项会报错;传错类型会报错;定义 emits 后,事件名和参数类型不匹配会报错;使用 d.ts 后,原本没有提示的模块能获得类型约束。开发时这些错误由 Volar 在 IDE 中即时显示;打包时由 vue-tsc 再检查一遍。这样既保证 Vite 开发速度,又保证最终类型无误。易错点是:不要用类型声明掩盖真实错误,也不要把开发期提示和打包期校验混为一谈。
课件图示








代码示例
以下代码均基于
npm create vite@latest vue-ts-pro -- --template vue-ts创建的项目,文件放在src/下即可直接运行。
示例一:为 props 标注类型 + 设置默认值(Button 组件)
src/components/MyButton.vue
vue
<script setup lang="ts">
// 第一步:先用 type 定义一个 props 的对象结构
type Props = {
color: string // 必填,且必须是 string
size?: string // 可选
btnType?: 'success' | 'danger' | 'warning' // 字面量联合类型,三选一,可选
}
// 第二步:defineProps 通过泛型传入类型;withDefaults 提供默认值
const props = withDefaults(defineProps<Props>(), {
size: 'middle', // 用户不传就用 middle,传了以用户的为准
btnType: 'success'
})
</script>
<template>
<button>{{ props.color }} / {{ props.size }} / {{ props.btnType }}</button>
</template>src/App.vue
vue
<script setup lang="ts">
import MyButton from './components/MyButton.vue'
</script>
<template>
<!-- color 必填且必须是 string,不传会报错 -->
<MyButton color="red" />
<!-- size、btnType 不传就用默认值 middle / success -->
<MyButton color="green" size="big" btnType="danger" />
<!-- 下面这行会报类型错误:btnType 只能是 success | danger | warning -->
<!-- <MyButton color="green" btnType="succes" /> -->
</template>关键行与易错点
type Props = {...}:只是定义对象结构,color不带?就是必填项;size?、btnType?带问号才是可选。defineProps<Props>():必须传泛型参数,把上面定义好的类型整体传进去,这是类型标注的第二步。withDefaults(defineProps<Props>(), { ... }):第一个参数是defineProps()的调用结果,不是类型本身;第二个参数对象的 key 必须和 props 同名。- 默认值只对可选属性有意义,
color是必填项,因此没有给它默认值。 'success' | 'danger' | 'warning'是字面量联合类型,传其他字符串(例如少写一个 s 的succes)会立即报错,这正是"类型安全"的体现。- 模板里可以直接写
size,也可以写props.size,但脚本中访问 props 里的值要用props.xxx。
示例二:为 emits 标注类型(子组件触发多个自定义事件)
src/components/ChildComp.vue
vue
<script setup lang="ts">
// 列表项类型
type ListItem = {
id: number
name: string
}
// 第一步:定义 emits 类型,格式固定 —— (e: '事件名', 参数: 类型): void
type Emits = {
(e: 'getMessage', message: string): void
(e: 'getList', list: ListItem[]): void
}
// 第二步:defineEmits 通过泛型传入
const emit = defineEmits<Emits>()
const handleClick = () => {
emit('getMessage', 'I am message') // 参数必须是 string
emit('getList', [{ id: 1001, name: '冬季棉袜' }]) // 参数必须是 ListItem[]
}
</script>
<template>
<button @click="handleClick">触发事件</button>
</template>src/App.vue
vue
<script setup lang="ts">
import ChildComp from './components/ChildComp.vue'
const onMessage = (msg: string) => console.log('getMessage:', msg)
const onList = (list: { id: number; name: string }[]) => console.log('getList:', list)
</script>
<template>
<ChildComp @get-message="onMessage" @get-list="onList" />
</template>关键行与易错点
type Emits里每一项都是(e: '事件名', 参数: 类型): void的固定写法,返回值写void。- 同一个组件触发多个自定义事件,就并排写多条,各是各的,不要合并成一个参数。
defineEmits<Emits>()传的是类型,不是数组;基础阶段的defineEmits(['xxx'])写法在 TS 下拿不到参数类型校验。emit('getMessage', 100)会报错,因为第二个参数被约束成string;事件名写成'getMssage'也会报错,这正是"约束事件名 + 约束参数类型"两个作用。- 父组件模板监听时要用 kebab-case:
@get-message、@get-list。
示例三:自定义类型声明文件 d.ts 共享类型
src/types/data.d.ts
ts
// 只能写类型信息,不能写 function 实现、循环等可运行逻辑
export interface Goods {
id: string
name: string
}src/App.vue
vue
<script setup lang="ts">
import { ref } from 'vue'
// 导入时加 type 关键词,明确这是一个类型而不是普通模块
import type { Goods } from './types/data'
const goods = ref<Goods>({
id: '1001', // 必须是 string,写成数字 1001 会报错
name: '冬季棉袜'
})
</script>
<template>
<div>{{ goods.id }} - {{ goods.name }}</div>
</template>可选:为 JS 文件补充类型(同一目录同名文件)
src/utils/add/index.js
js
export const add = (a, b) => a + bsrc/utils/add/index.d.ts
ts
declare function add(a: number, b: number): number
export { add }关键行与易错点
.d.ts文件里只能包含类型信息,不能写function的具体实现、不能写循环;打包时它也不会被编译成 JS,只做类型检查。- 业务里最常用的场景是共享类型:把多处复用的类型统一放到
types/xxx.d.ts中export出去,哪里用哪里import。 - 导入类型时建议加
type关键词(import type { Goods }),一眼区分"类型"和"普通模块",也能被编译期完全擦除。 ref<Goods>({...})里少了id或者id写成数字都会报错,这是类型约束在起作用。- 给 JS 文件补类型时,
.d.ts必须与 JS 文件同名且同目录,并用declare声明;转写稿中的add示例正是靠index.d.ts才能在使用index.js时获得提示。 - 注意区分:
.ts文件可以写类型 + 逻辑,会被编译;.d.ts只写类型,不参与编译。
常见错误
创建项目时模板没生效,生成的是纯 TS 或 React 模板 现象:执行
npm create vite@latest vue-ts-pro --template vue-ts后项目里没有.vue文件,或直接进入交互式问答。 原因:--template被 npm 当成自己的参数吃掉,没有透传给 vite。 修法:在项目名和模板参数之间补上--,即npm create vite@latest vue-ts-pro -- --template vue-ts。IDE 里没有
.vue类型提示,但npm run build才报错 现象:开发时 props 传错不飘红,打包时vue-tsc一次性报一堆类型错误。 原因:没装 Volar(或仍在用 Vetur),或者没装 TypeScript Vue Plugin 导致 TS 无法识别import *.vue。 修法:禁用 Vetur,安装 Volar + TypeScript Vue Plugin,重启编辑器;确认package.json的 build 脚本走的是vue-tsc而不是tsc。defineProps定义了类型却拿不到 props 现象:defineProps<Props>()之后在 script 里写color报 “Cannot find name 'color'”。 原因:宏的返回值没有被接收,defineProps只是声明,不会自动把属性挂到作用域。 修法:const props = defineProps<Props>(),使用时写props.color。设置默认值时写法错误 现象:给可选 prop 写默认值时编译报错,或默认值完全不生效。 原因:把默认值写进类型(如
color?: string = 'red'),或者withDefaults的第一个参数直接传了类型名。 修法:withDefaults(defineProps<Props>(), { size: 'middle' })——第一个参数必须是defineProps的调用,第二个参数才是默认值对象。defineEmits用字符串数组声明,事件名拼错不报错 现象:emit('getMesage', ...)少个字母也能跑,父组件收不到,排查半天。 原因:defineEmits(['getMessage'])这种数组写法只做运行时校验,不做类型约束,且参数类型完全丢失。 修法:改用调用签名defineEmits<{ (e: 'getMessage', msg: string): void }>(),事件名和参数类型同时被约束。在
.d.ts里写可执行代码 现象:编译报 “An implementation cannot be declared in ambient contexts”。 原因:.d.ts只能承载类型信息,不能有函数体、if、循环等逻辑。 修法:只写declare function add(a: number, b: number): number并export,实现留在同名.js/.ts里。给 JS 库装了
@types/xxx仍然报“找不到模块的声明文件” 现象:装完类型包,红波浪线还在。 原因:TS 语言服务缓存未刷新,或包被装进了dependencies而类型检查配置未覆盖。 修法:装到开发依赖npm i -D @types/xxx,然后重启编辑器 / 执行 VS Code 的 “Restart TS Server”。axios 泛型传错层级,
res.data没有提示 现象:request<Article[]>(...)后res.data.xxx一片红,或res.data被推断成any。 原因:request<T>的T约束的是res.data的类型,也就是后端响应体本身,不是里面的某个字段。 修法:泛型传完整的响应体类型request<ChannelRes>(...),再通过res.data.data.channels逐层取。ref([])推成never[],赋值时报类型不兼容 现象:list.value = res.data.data.results报 “不能将 xxx 分配给 never[]”。 原因:空数组字面量无法推断元素类型,TS 只能给never[]。 修法:显式传泛型ref<ArticleItem[]>([]),或ref([] as ArticleItem[])。
练习
props + 默认值:为
Card组件定义title: string(必填)、count?: number(默认 0)、tags?: string[](默认[])。要求:父组件不传title必须报错;不传count时组件内拿到 0。写出完整 script 部分。emits 类型标注:某子组件需触发
change(id: number)与update(payload: { id: number; name: string })两个事件。写出类型定义与emit调用,并说明为什么这里不能用defineEmits(['change', 'update'])。d.ts 补类型:项目里有
src/utils/format.js,导出一个formatDate(date, fmt)。请在同目录写一个同名.d.ts,让import { formatDate } from './utils/format'有完整提示;并回答:如果把文件里的export去掉会发生什么?axios + ref 类型贯通:后端返回
{ code, message, data: { list: ArticleItem[]; total: number } }。写出ArticleItem、响应体类型、ref声明和request调用,并标出「赋值语句为什么天然不会报类型错误」。排错:下面代码有 3 处问题,指出并改正。
tsconst props = withDefaults<Props>({ size: 'middle' }) interface Props { color: string; size?: string } const emit = defineEmits(['submit']) emit('submit', 100)
原视频对照
| # | 视频 | 时长 |
|---|---|---|
| 1 | 为props标注类型 | 12:37 |
| 2 | 为组件的emits的标注类型 | 09:50 |
| 3 | 类型声明文件d.ts-01 | 11:50 |
| 4 | 类型声明文件d.ts-02 | 09:09 |
| 5 | 综合案例-需求熟悉和环境准备 | 07:44 |
| 6 | 综合案例-实现频道列表渲染 | 16:00 |
| 7 | 综合案例-文章列表基础渲染 | 12:39 |
小结
- 创建 Vue+TS 环境的关键只有一个字——
--:漏掉它,模板参数就传不进去。 - 开发期的类型反馈靠 Volar,打包期的兜底靠
vue-tsc,两者分工不能互相替代;只装了其中一个,都会出现「要么开发无提示,要么上线才炸」。 defineProps/defineEmits是编译宏,类型写在泛型里、值接在变量上,缺了接收这一步,类型再对也用不了。- 可选 prop 才有默认值的意义,默认值只能通过
withDefaults的第二个参数给,且第一个参数必须是defineProps()的调用结果。 - emits 一律用调用签名而非字符串数组:数组只保运行时,签名才同时锁住事件名和参数类型。
.d.ts是纯类型文件,不进打包产物、不能写逻辑;它有三种来源——库自带(TS 编写)、@types/*补充(JS 编写)、TS 内置(标准 API)。- 业务里手写
.d.ts的主场景是共享类型,导入时加type关键字,语义更清晰也便于摇树。 - 整套 TS 业务的落地心法只有一句:响应式数据和接口返回值都按同一份接口文档定义类型,赋值时自然匹配,渲染时自动获得提示。