vue3+ts开发干货笔记
总结一下在vue3中ts的使用。当篇记录部分来自于vue官网,记录一下,算是加深印象吧。
纯干笔记,不断补充,想到什么写什么,水平有限,欢迎评论指正!
另外,如果想了解更多ts相关知识,可以参考我的其他笔记:
- TSConfig 配置(tsconfig.json)
- ts相关笔记(基础必看)
- ts相关笔记(Partial、Required、Readonly、Record、Exclude、Extract)
- ts相关笔记(类型层级)
- ts相关笔记(extends、infer、Pick、Omit)
类型标注
props
<script setup lang="ts">
const props = defineProps({foo: { type: String, required: true },bar: Number
})props.foo // string
props.bar // number | undefined
</script>
可以通过更直接的泛型定义
<script setup lang="ts">
const props = defineProps<{foo: stringbar?: number
}>()
</script>
也可以将 props 的类型移入一个单独的接口中
<script setup lang="ts">
interface Props {foo: stringbar?: number
}const props = defineProps<Props>()
</script>
把定义单独放到另一个文件中,引入使用
<script setup lang="ts">
import type { Props } from './foo'const props = defineProps<Props>()
</script>
设置默认值— 通过 withDefaults 编译器宏
export interface Props {msg?: stringlabels?: string[]
}const props = withDefaults(defineProps<Props>(), {msg: 'hello',labels: () => ['one', 'two']
})
复杂类型的props
<script setup lang="ts">
interface Book {title: stringauthor: stringyear: number
}const props = defineProps<{book: Book
}>()
</script>
也可以借助PropType 工具类型
import type { PropType } from 'vue'const props = defineProps({book: Object as PropType<Book>
})
没有使用 < script setup > 时,使用 defineComponent()
import { defineComponent } from 'vue'export default defineComponent({props: {message: String},setup(props) {props.message // <-- 类型:string}
})
import { defineComponent } from 'vue'
import type { PropType } from 'vue'export default defineComponent({props: {book: Object as PropType<Book>}
})
emits
<script setup lang="ts">
// 运行时
const emit = defineEmits(['change', 'update'])// 基于选项
const emit = defineEmits({change: (id: number) => {// 返回 `true` 或 `false`// 表明验证通过或失败},update: (value: string) => {// 返回 `true` 或 `false`// 表明验证通过或失败}
})// 基于类型
const emit = defineEmits<{(e: 'change', id: number): void(e: 'update', value: string): void
}>()
</script>
vue3.3+: 可选的、更简洁的语法
const emit = defineEmits<{change: [id: number]update: [value: string]
}>()
若没有使用 < script setup >,defineComponent() 也可以根据 emits 选项推导暴露在 setup 上下文中的 emit 函数的类型:
import { defineComponent } from 'vue'export default defineComponent({emits: ['change'],setup(props, { emit }) {emit('change') // <-- 类型检查 / 自动补全}
})
ref
ref 会根据初始化时的值推导其类型:
import { ref } from 'vue'// 推导出的类型:Ref<number>
const year = ref(2020)// => TS Error: Type 'string' is not assignable to type 'number'.
year.value = '2020'
或者在调用 ref() 时传入一个泛型参数,来覆盖默认的推导行为
// 得到的类型:Ref<string | number>
const year = ref<string | number>('2020')year.value = 2020 // 成功!
另外也可以通过引入 Ref 来指定类型
import { ref } from 'vue'
import type { Ref } from 'vue'const year: Ref<string | number> = ref('2020')year.value = 2020 // 成功!
当指定了一个泛型参数但没有给出初始值,那么最后得到的就将是一个包含 undefined 的联合类型:
// 推导得到的类型:Ref<number | undefined>
const n = ref<number>()
reactive
reactive() 也会隐式地从它的参数中推导类型:
import { reactive } from 'vue'// 推导得到的类型:{ title: string }
const book = reactive({ title: 'Vue 3 指引' })
要显式地标注一个 reactive 变量的类型,可以使用接口:
import { reactive } from 'vue'interface Book {title: stringyear?: number
}const book: Book = reactive({ title: 'Vue 3 指引' })
TIP
不推荐使用 reactive() 的泛型参数,因为处理了深层次 ref 解包的返回值与泛型参数的类型不同。
computed
computed() 会自动从其计算函数的返回值上推导出类型:
import { ref, computed } from 'vue'const count = ref(0)// 推导得到的类型:ComputedRef<number>
const double = computed(() => count.value * 2)// => TS Error: Property 'split' does not exist on type 'number'
const result = double.value.split('')
还可以通过泛型参数显式指定类型:
const double = computed<number>(() => {// 若返回值不是 number 类型则会报错
})
事件处理函数
<script setup lang="ts">
function handleChange(event) {// `event` 隐式地标注为 `any` 类型console.log(event.target.value)
}
</script><template><input type="text" @change="handleChange" />
</template>
没有类型标注时,这个 event 参数会隐式地标注为 any 类型。这也会在 tsconfig.json 中配置了 “strict”: true 或 “noImplicitAny”: true 时报出一个 TS 错误。因此,建议显式地为事件处理函数的参数标注类型。此外,你在访问 event 上的属性时可能需要使用类型断言:
function handleChange(event: Event) {console.log((event.target as HTMLInputElement).value)
}
provide/inject
provide 和 inject 通常会在不同的组件中运行。要正确地为注入的值标记类型,Vue 提供了一个 InjectionKey 接口,它是一个继承自 Symbol 的泛型类型,可以用来在提供者和消费者之间同步注入值的类型:
import { provide, inject } from 'vue'
import type { InjectionKey } from 'vue'const key = Symbol() as InjectionKey<string>provide(key, 'foo') // 若提供的是非字符串值会导致错误const foo = inject(key) // foo 的类型:string | undefined
建议将注入 key 的类型放在一个单独的文件中,这样它就可以被多个组件导入。
当使用字符串注入 key 时,注入值的类型是 unknown,需要通过泛型参数显式声明:
const foo = inject<string>('foo') // 类型:string | undefined
注意注入的值仍然可以是 undefined,因为无法保证提供者一定会在运行时 provide 这个值。
当提供了一个默认值后,这个 undefined 类型就可以被移除:
const foo = inject<string>('foo', 'bar') // 类型:string
如果你确定该值将始终被提供,则还可以强制转换该值:
const foo = inject('foo') as string
模版引用
模板引用需要通过一个显式指定的泛型参数和一个初始值 null 来创建:
<script setup lang="ts">
import { ref, onMounted } from 'vue'const el = ref<HTMLInputElement | null>(null)onMounted(() => {el.value?.focus()
})
</script><template><input ref="el" />
</template>
可以通过类似于 MDN 的页面来获取正确的 DOM 接口。
注意为了严格的类型安全,有必要在访问 el.value 时使用可选链或类型守卫。这是因为直到组件被挂载前,这个 ref 的值都是初始的 null,并且在由于 v-if 的行为将引用的元素卸载时也可以被设置为 null。
组件模版引用
有时,你可能需要为一个子组件添加一个模板引用,以便调用它公开的方法。举例来说,我们有一个 MyModal 子组件,它有一个打开模态框的方法:
<!-- MyModal.vue -->
<script setup lang="ts">
import { ref } from 'vue'const isContentShown = ref(false)
const open = () => (isContentShown.value = true)defineExpose({open
})
</script>
为了获取 MyModal 的类型,我们首先需要通过 typeof 得到其类型,再使用 TypeScript 内置的 InstanceType 工具类型来获取其实例类型:
<!-- App.vue -->
<script setup lang="ts">
import MyModal from './MyModal.vue'const modal = ref<InstanceType<typeof MyModal> | null>(null)const openModal = () => {modal.value?.open()
}
</script>
注意,如果你想在 TypeScript 文件而不是在 Vue SFC 中使用这种技巧,需要开启 Volar 的 Takeover 模式。
如果组件的具体类型无法获得,或者你并不关心组件的具体类型,那么可以使用 ComponentPublicInstance。这只会包含所有组件都共享的属性,比如 $el。
import { ref } from 'vue'
import type { ComponentPublicInstance } from 'vue'const child = ref<ComponentPublicInstance | null>(null)
tsconfig.json
tsconfig.json是 TypeScript 项目的配置文件,放在项目的根目录下。指定了编译项目所需的根目录下的文件以及编译选项。官网参考
配置解析
{/* 根选项 */"include": ["./src/**/*"], // 指定被编译文件所在的目录"exclude": [], // 指定不需要被编译的目录//使用小技巧:在填写路径时 ** 表示任意目录, * 表示任意文件。/* 项目选项 */"compilerOptions": {"target": "ES6", // 目标语言的版本"module": "commonjs", // 生成代码的模板标准"lib": ["DOM", "ES5", "ES6", "ES7", "ScriptHost"], // TS需要引用的库"outDir": "./dist", // 指定输出目录"rootDir": "./", // 指定输出文件目录(用于输出),用于控制输出目录结构"allowJs": true, // 允许编译器编译JS,JSX文件"checkJs": true, // 允许在JS文件中报错,通常与allowJS一起使用"removeComments": true, // 删除注释"esModuleInterop": true, // 允许export=导出,由import from 导入/* 严格检查选项 */"strict": true, // 开启所有严格的类型检查"alwaysStrict": true, // 在代码中注入'use strict'"noImplicitAny": true, // 不允许隐式的any类型"noImplicitThis": true, // 不允许this有隐式的any类型"strictNullChecks": true, // 不允许把null、undefined赋值给其他类型的变量"strictBindCallApply": true, // 严格的bind/call/apply检查"strictFunctionTypes": true, // 不允许函数参数双向协变"strictPropertyInitialization": true, // 类的实例属性必须初始化/* 额外检查 */"noUnusedLocals": true, //是否检查未使用的局部变量"noUnusedParameters": true, //是否检查未使用的参数"noImplicitReturns": true, //检查函数是否不含有隐式返回值"noImplicitOverride": true, //是否检查子类继承自基类时,其重载的函数命名与基类的函数不同步问题"noFallthroughCasesInSwitch": true, //检查switch中是否含有case没有使用break跳出"noUncheckedIndexedAccess": true, //是否通过索引签名来描述对象上有未知键但已知值的对象"noPropertyAccessFromIndexSignature": true, //是否通过" . “(obj.key) 语法访问字段和"索引”( obj[“key”]), 以及在类型中声明属性的方式之间的一致性/* 实验选项 */"experimentalDecorators": true, //是否启用对装饰器的实验性支持,装饰器是一种语言特性,还没有完全被 JavaScript 规范批准"emitDecoratorMetadata": true, //为装饰器启用对发出类型元数据的实验性支持/* 高级选项 */"forceConsistentCasingInFileNames": true, //是否区分文件系统大小写规则"extendedDiagnostics": false, //是否查看 TS 在编译时花费的时间"noEmitOnError": true, //有错误时不进行编译"resolveJsonModule": true //是否解析 JSON 模块}
}
示例
{"compilerOptions": {"target": "ESNext","module": "ESNext","moduleResolution": "bundler","strict": false,"jsx": "preserve","importHelpers": true,"experimentalDecorators": true,"strictFunctionTypes": false,"skipLibCheck": true,"esModuleInterop": true,"isolatedModules": true,"allowSyntheticDefaultImports": true,"forceConsistentCasingInFileNames": true,"sourceMap": true,"baseUrl": ".","allowJs": false,"resolveJsonModule": true,"lib": ["ESNext","DOM"],"paths": {"@/*": ["src/*"],"@build/*": ["build/*"]},"types": ["node","vite/client","element-plus/global","@pureadmin/table/volar","@pureadmin/descriptions/volar"]},"include": ["mock/*.ts","src/**/*.ts","src/**/*.tsx","src/**/*.vue","types/*.d.ts","vite.config.ts"],"exclude": ["dist","**/*.js","node_modules"]
}
全局类型声明
global.d.ts和index.d.ts
在 global.d.ts (opens new window)和 index.d.ts (opens new window)文件中编写的类型可直接在 .ts、.tsx、.vue 中使用


shims-tsx.d.ts
该文件是为了给 .tsx 文件提供类型支持,在编写时能正确识别语法
import Vue, { VNode } from "vue";declare module "*.tsx" {import Vue from "compatible-vue";export default Vue;
}declare global {namespace JSX {interface Element extends VNode {}interface ElementClass extends Vue {}interface ElementAttributesProperty {$props: any;}interface IntrinsicElements {[elem: string]: any;}interface IntrinsicAttributes {[elem: string]: any;}}
}
shims-vue.d.ts
.vue、.scss 文件不是常规的文件类型,typescript 无法识别,所以我们需要通过下图的代码告诉 typescript 这些文件的类型,防止类型报错

另外,项目开发,我们可能需要安装一些库或者插件什么的,当它们对 typescript 支持不是很友好的时候,就会出现下图的情况

解决办法就是将这些通过 declare module “包名” 的形式添加到 shims-vue.d.ts 中去,如下图

全局导入的组件获取类型提示
从 npm下载的组件库或者第三方库
也就是您使用 pnpm add 添加的包,比如 @pureadmin/table ,我们只需要将这个包提供的全局类声明文件导入到 tsconfig.json 的 types 配置项

然后重启编辑器即可,如下图导入前和导入后的效果对比
导入前,pure-table 无高亮且鼠标覆盖无类型提示

导入后,pure-table 高亮且鼠标覆盖有类型提示

提示
当然这个导入前提是这个组件库或者第三方库是有导出全局类声明文件的。

平台内自定义的全局组件
封装的一个和权限相关的 Auth 组件举例
- 我们将 Auth 组件在 main.ts 中进行了全局注册

- 然后将 Auth 组件在 global-components.d 中引入,所有的全局组件都应该在 GlobalComponents 下引入才可获得类型支持,如下图

- 最后我们直接写 xxx 就可以得到类型提示啦,如下图

相关文章:
vue3+ts开发干货笔记
总结一下在vue3中ts的使用。当篇记录部分来自于vue官网,记录一下,算是加深印象吧。 纯干笔记,不断补充,想到什么写什么,水平有限,欢迎评论指正! 另外,如果想了解更多ts相关知识&…...
Android开发新的一年Flag
在新的一年里,为了提升Android开发技能,实现更优质的应用程序,我们制定了2024的新年Flag。这些Flag涵盖了技术学习、代码优化、架构升级、用户体验等多个方面,旨在帮助我们成为更优秀的Android开发者。 1. 学习新技术 1.1. Andr…...
好的OODA循环与快慢无关
OODA循环是指观察(Observe)、导向(Orient)、决策(Decide)和行动(Act)这四个步骤的循环过程。它是一种决策和行动的框架,旨在帮助个人或组织更快地适应和应对变化。 OODA循…...
Android 车联网——CarUserService介绍(十三)
一、简介 CarUserService 是 Android 汽车平台的一个组件,它用于管理和提供车辆用户信息。该组件可以让开发者创建和管理与车辆用户相关的数据和配置,包括车辆拥有者和乘客的个人信息、偏好设置、用户偏好配置文件等。 CarUserService 提供了以下功能和特性: 用户配置管理:…...
【开题报告】基于微信小程序的母婴商品仓库管理系统的设计与实现
1.选题背景 随着社会经济的发展和家庭生活水平的提高,母婴商品市场逐渐兴起。然而,传统的母婴商品仓库管理方式存在着许多问题,如信息不透明、操作繁琐等。为了提高仓库管理的效率和准确性,基于微信小程序的母婴商品仓库管理系统…...
分布式锁相关问题(三)
Redis实战精讲-13小时彻底学会Redis 一、什么是分布式锁? 要介绍分布式锁,首先要提到与分布式锁相对应的是线程锁、进程锁。 l 线程锁:主要用来给方法、代码块加锁。当某个方法或代码使用锁,在同一时刻仅有一个线程执行该方法或该…...
grep!Linux系统下强大的文本搜索工具!
grep!Linux系统下强大的文本搜索工具! grep是一个强大的文本搜索工具,它可以在文件中查找包含指定字符串的行。grep的基本语法如下: grep [选项] "搜索字符串" 文件名其中,选项可以是以下几种:…...
(学习打卡1)重学Java设计模式之设计模式介绍
前言:听说有本很牛的关于Java设计模式的书——重学Java设计模式,然后买了(*^▽^*) 开始跟着小傅哥学Java设计模式吧,本文主要记录笔者的学习笔记和心得。 打卡!打卡! 设计模式介绍 一、设计模式是什么? …...
docker 部署教学版本
文章目录 一、docker使用场景及常用命令1)docker使用场景2)rocky8(centos8)安装 docker3)docker 常用命令补充常用命令 二、 单独部署每个镜像,部署spring 应用镜像推荐(2023-12-18)1、 安装使用 mysql1.1 …...
2023春季李宏毅机器学习笔记 05 :机器如何生成图像
资料 课程主页:https://speech.ee.ntu.edu.tw/~hylee/ml/2023-spring.phpGithub:https://github.com/Fafa-DL/Lhy_Machine_LearningB站课程:https://space.bilibili.com/253734135/channel/collectiondetail?sid2014800 一、图像生成常见模型…...
C#和C++存储 和 解析 bin 文件
C 解析 bin 文件 // C 解析 bin 文件 #include <stdio.h>int main() {FILE *file; // 定义文件指针file fopen("example.bin", "rb"); // 打开二进制文件(只读模式)if (file NULL) {printf("无法打开文件\n");re…...
【React系列】Redux(二)中间件
本文来自#React系列教程:https://mp.weixin.qq.com/mp/appmsgalbum?__bizMzg5MDAzNzkwNA&actiongetalbum&album_id1566025152667107329) 一. 中间件的使用 1.1. 组件中异步请求 在之前简单的案例中,redux中保存的counter是一个本地定义的数据…...
YOLOv8改进 | 2023Neck篇 | 利用Gold-YOLO改进YOLOv8对小目标检测
一、本文介绍 本文给大家带来的改进机制是Gold-YOLO利用其Neck改进v8的Neck,GoLd-YOLO引入了一种新的机制——信息聚集-分发(Gather-and-Distribute, GD)。这个机制通过全局融合不同层次的特征并将融合后的全局信息注入到各个层级中,从而实现更高效的信息交互和融合。这种…...
ubuntu环境安装配置nginx流程
今天分享ubuntu环境安装配置nginx流程 一、下载安装 1、检查是否已经安装 nginx -v 结果 2、安装 apt install nginx-core 过程 查看版本:nginx -v 安装路径:whereis nginx nginx文件安装完成之后的文件位置: /usr/sbin/nginx…...
【LMM 010】MiniGPT-v2:使用独特的标识符实现视觉语言多任务学习的统一的多模态大模型
论文标题:MiniGPT-v2: Large Language Model As a Unified Interface for Vision-Language Multi-task Learning 论文作者:Jun Chen, Deyao Zhu, Xiaoqian Shen, Xiang Li, Zechun Liu, Pengchuan Zhang, Raghuraman Krishnamoorthi, Vikas Chandra, Yun…...
人工智能如何重塑金融服务业
在体验优先的世界中识别金融服务业中的AI使用场景 人工智能(AI)作为主要行业的大型组织的重要业务驱动力,持续受到关注。众所周知,传统金融服务业在采用新技术方面相对滞后,一些组织使用的还是上世纪50年代和60年代发…...
Iterable 对象转换为 Stream 对象
在 Java 8 中,可以使用 Stream API 来对集合进行操作。要将 Iterable 对象转换为 Stream 对象,可以使用 StreamSupport 类的 stream() 方法。具体来说,可以按照以下步骤进行转换: 调用 Spliterators.spliteratorUnknownSize(iter…...
基于Java+SpringBoot+vue+elementUI私人健身教练预约管理系统设计实现
基于JavaSpringBootvueelementUI私人健身教练预约管理系统设计实现 欢迎点赞 收藏 ⭐留言 文末获取源码联系方式 文章目录 基于JavaSpringBootvueelementUI私人健身教练预约管理系统设计实现一、前言介绍:二、系统设计:2.1 性能需求分析2.2 B/S架构&…...
2024,启动(回顾我的2023)
零.前言 打开博客想写个年度总结,发现已经半年没有更新文章了,排名从几千掉到了几万,不过数据量还是不错的。 时间过得可真快,2023年是充满动荡的一年,上半年gpt横空出世,下半年各种翻车暴雷吃瓜吃到嘴软…...
Web网页开发-盒模型-笔记
1.CSS的三种显示方式 (1)块级元素:标签所占区域默认为一行 特点:一行一个 可设宽高 (2)行内元素:标签所占区域由内容顶开,行内元素无法使用text-align 特点:一行多个 不可设宽高,margin上下和padding上下都不能改变位…...
LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明
LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造,完美适配AGV和无人叉车。同时,集成以太网与语音合成技术,为各类高级系统(如MES、调度系统、库位管理、立库等)提供高效便捷的语音交互体验。 L…...
【杂谈】-递归进化:人工智能的自我改进与监管挑战
递归进化:人工智能的自我改进与监管挑战 文章目录 递归进化:人工智能的自我改进与监管挑战1、自我改进型人工智能的崛起2、人工智能如何挑战人类监管?3、确保人工智能受控的策略4、人类在人工智能发展中的角色5、平衡自主性与控制力6、总结与…...
React Native 导航系统实战(React Navigation)
导航系统实战(React Navigation) React Navigation 是 React Native 应用中最常用的导航库之一,它提供了多种导航模式,如堆栈导航(Stack Navigator)、标签导航(Tab Navigator)和抽屉…...
【JVM】- 内存结构
引言 JVM:Java Virtual Machine 定义:Java虚拟机,Java二进制字节码的运行环境好处: 一次编写,到处运行自动内存管理,垃圾回收的功能数组下标越界检查(会抛异常,不会覆盖到其他代码…...
如何将联系人从 iPhone 转移到 Android
从 iPhone 换到 Android 手机时,你可能需要保留重要的数据,例如通讯录。好在,将通讯录从 iPhone 转移到 Android 手机非常简单,你可以从本文中学习 6 种可靠的方法,确保随时保持连接,不错过任何信息。 第 1…...
k8s业务程序联调工具-KtConnect
概述 原理 工具作用是建立了一个从本地到集群的单向VPN,根据VPN原理,打通两个内网必然需要借助一个公共中继节点,ktconnect工具巧妙的利用k8s原生的portforward能力,简化了建立连接的过程,apiserver间接起到了中继节…...
Redis数据倾斜问题解决
Redis 数据倾斜问题解析与解决方案 什么是 Redis 数据倾斜 Redis 数据倾斜指的是在 Redis 集群中,部分节点存储的数据量或访问量远高于其他节点,导致这些节点负载过高,影响整体性能。 数据倾斜的主要表现 部分节点内存使用率远高于其他节…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
蓝桥杯3498 01串的熵
问题描述 对于一个长度为 23333333的 01 串, 如果其信息熵为 11625907.5798, 且 0 出现次数比 1 少, 那么这个 01 串中 0 出现了多少次? #include<iostream> #include<cmath> using namespace std;int n 23333333;int main() {//枚举 0 出现的次数//因…...
Springboot社区养老保险系统小程序
一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,社区养老保险系统小程序被用户普遍使用,为方…...
