1, 了解TypeScript
TS是带类型语法的JS : TypeScript
是一种带有 类型语法
的 JavaScript 语言,在任何使用 JavaScript 的开发场景中都可以使用。
JavaScript 代码 没有明确的类型 let age = 18
TypeScript 代码 有明确的类型,可以指定age是number类型(数值类型) let age: number = 18
注意:TS 需要编译才能在浏览器运行。
2,TS 优势:
- JS 是动态类型的编程语言,动态类型最大的特点就是它只能在
代码执行
期间做类型的相关检查,所以往往你发现问题的时候,已经晚了。 - TS 是静态类型的编程语言,代码会先进行编译然后去执行,在
代码编译
期间做类型的相关检查,如果有问题编译是不通过的,也就暴露出了问题。 - 配合 VSCode 等开发工具,TS 可以提前到在
编写代码
的时候就能发现问题,更准确更快的处理错误。
TS优势总结
- 更早发现错误,提高开发效率
- 随时随地提示,增强开发体验
- 强大类型系统,代码可维护性更好,重构代码更容易
- 类型推断机制,减少不必要类型注解,让编码更简单
- 最后:Vue3源码TS重写,React和TS完美配合,Angular默认支持TS,大中型前端项目首选。
3,如何使用 TypeScript 编译ts代码
全局安装:
# npm 安装
npm i -g typescript
# yarn 安装
yarn global add typescript
# 部分mac电脑安装需要sudo权限
# sudo npm i -g typescript
# sudo yarn global add typescript
查看版本:
tsc -v
编译ts流程:新建hello.ts文件 ====> 在当前根目录打开命令窗口,执行tsc hello.ts
命令,同级目录生成 hello.js
文件===>验证一下
- 在开发中:一般使用
webpack
vite
等工具自动构建编译。
创建 vue-ts 项目
任选一种方式:
# npm 6.x
npm create vite@latest my-vue-ts-app --template vue-ts
# npm 7+, extra double-dash is needed:
npm create vite@latest my-vue-ts-app -- --template vue-ts
# yarn
yarn create vite my-vue-ts-app --template vue-ts
# pnpm
pnpm create vite my-vue-ts-app --template vue-ts
4,TypeScript 核心
首先了解一下js的数据类型有哪些:js的数据类型一共有8种,
简单数据类型:number, string ,boolean, null,undefined,Symbol,BigInt
复杂数据类型:Object【Object是个大类,function函数、array数组、date日期...等都归属于Object】
一 类型注解
- 变量后面约定类型的语法,就是类型注解
约定了什么类型,就只能给该变量赋值什么类型的值,否则报错。
// 约定变量 age 的类型为 number 类型
let age: number = 18;
age = 19;
二 数组类型
// 数组类型
let arr: Array<number> = [1, 2, 3, 4, 5]
let arr1: number[] = [1, 2, 3, 4, 5]
let arr2: string[] = ['1', '2', '3']
三 联合类型:
- 类型与类型之间使用
|
连接,代表类型可以是它们当中的其中一种,这种类型叫:联合类型
第一种:
let count: number | string = 2 // count的值只能是number类型或者string类型
第二种:
let obj: { id: number, name: string } = { id: 1, name: 'jack' }
let obj2: { id: number, name: string } | number | number[ ] = { id: 1, name: 'jack' }
obj2 的值类型可以是这几种:{ id: number, name: string } | number | number[ ]
第三种:
let arr3: ( number | string | number[ ] )[ ] = [1, 2, 3, '4', [1, ‘2’] ]
第四种:
let list: {
id: number;
name: string;
hobby: string[];
family: {
mather: string;
};
} = {
id: 1,
name: 'jack',
hobby: ['游泳', '唱歌'],
family: {
mather: '母亲'
}
}
四 函数类型
- 给函数指定类型,其实是给
参数
和返回值
指定类型。 - 两种写法:
- 在函数基础上
分别指定
参数和返回值类型 - 使用类型别名
同时指定
参数和返回值类型
- 在函数基础上
// 普通函数类型 定义函数的参数类型是number,返回值类型是string
function add(a: number, b: number): string {
return '12'
}
add(1, 2)
// 箭头函数 定义函数的参数 返回值类型都是number
const add1 = (a: number, b: number): number => {
return 12
}
add1(1, 2)
// 使用函数类型别名
同时指定
参数和返回值类型:type F = (a: number, b: number) => string
const add2: F = (a, b) => {
return '12'
}
add2(1, 2)
const add2: (a: number, b: number) => number= (a, b) => {
return a
}
add2(1, 2)
五 类型别名:
- 当同一类型(复杂)被多次使用时,可以通过类型别名,
简化
该类型的使用
// 对象类型别名
type N =number
type S=string
type T = {
id: N;
name: S;
hobby: S[ ];
family: {
mather: S;
}
say: () => string
fn(): void
}
let list: T = {
id: 1,
name: 'jack',
hobby: ['游泳', '唱歌'],
family: {
mather: '母亲'
},
say: () => {
return '123'
},
fn() {
return 456
}
}
// 函数类型别名:
type F = (a: number, b: number) => string
const add3: F = (a, b) => {
return '12'
}
add2(1, 2)
六 void 类型
对于返回值不确定的可以使用void void跟undefined是两种类型 类型是undefined 值只有undefined
例如: 后台返回的数据 不确定类型的情况下 可以用void 函数的返回值不确定类型的情况下也可以用void
type F1 = (a: number, b: number) => undefined
const add4: F1 = (a, b) => {
return undefined
}
add4(1, 2)
七 字面量类型
// 字面量类型
// let num:123=123 字母量类型
// num=456 会报错
const num2: 456 = 456
let num3: number = 789
num3 = 666
八 可选参数
- 如果函数的参数,可以传也可以不传,这种情况就可以使用
可选参数
语法,参数后加?
即可
// 第一种
type F2 = (a?: number, b?: number) => undefined
const add5: F2 = (a, b) => {
return undefined
}
add5() //可以不传参 在上面的a后面加?
// 函数的可选参数有两种情况:箭头函数跟普通函数 ?写的位置不一样 如下:
type list1 = {
id: number
name: string
hobby: string[]
family: {
mather: string
},
say?: () => string
fn?(): void
}
let list4: list1 = {
id: 1,
name: 'jack',
hobby: ['游泳', '唱歌'],
family: {
mather: '母亲'
},
// say: () => {
// return '123'
// },
// fn() {
// return 456
// }
}
九 接口 interface
interface
后面是接口名称,和类型别名的意思一样。简单来说,接口后面是接类型。
//接口 interface 没有等号 专门定义对象数据类型
interface Iperson {
id: number
name: string
}
const obj1: Iperson = {
id: 1,
name: 'jack'
}
// type 类型别名 有等号 可以定义所有数据类型
type Toby={
id:number
name:string
}
const obj1:Toby={
id:1,
name:'jack'
}
十 interface 继承
使用 extends 实现接口继承,达到类型复用
// 接口 interface 继承 extends 不用extends 就重复定义会合并
interface Iperson {
id: number
name: string
}
interface Iperson2 extends Iperson { //Iperson2 继承 Iperson 的类型
age: number
hobby: string[]
}
const obj1: Iperson2 = {
id: 1,
name: 'jack',
age: 18,
hobby: ['rap']
}
十一 type 交叉类型
// 类型别名 type & 继承 type不可以重复定义
type Tperson = {
id: number
name: string
}
type Tperson2 = Tperson & {
age: number
hobby: ['rap']
}
const obj3: Tperson2 = {
id: 1,
name: 'jack',
age: 18,
hobby: ['rap']
}
interface 和 type 的相同点和区别 小结:
- 它们都可以定义对象类型
- 它们都可以复用,interface 使用
extends
, type 使用&
- type 不能重复定义,interface 可以重复会合并
十二 类型推断 as
// 类型断言:as
// 但是我们明确知道获取的是一个 A 元素,可以通过 类型断言 给它指定一个更具体的类型。标签的类型
const aLink = document.getElementById('link') as HTMLAnchorElement //获取的元素是一个a标签
const img = document.getElementById('img') as HTMLImageElement
type T2 = {
id: number
name: string
arr: number[]
}
let obj7 = {} as T2 //类型断言
obj7.arr = [1, 2] //obj7. 后面会有提示 只能拿到以下3个
obj7.id = 3
obj7.name = 'jack'
// 类型推断: typeof 后面接值 ====> 返回的是 类型
const obj4 = {
id: 1,
name: 'jack'
}
type Tperson4 = typeof obj4 // 进行类型推断
let obj5 = {} as Tperson4 // 结合as类型断言
obj5.id = 2
obj.name = 'rose'
十三 any 类型
- 显式any情况:当变量的类型指定为 any 的时候,不会有任何错误,也不会有代码提示,TS会忽略类型检查
any
的使用越多,程序可能出现的漏洞越多,因此不推荐使用any
类型,尽量避免使用。
十四 类型推断 typeof keyof
- 在 TS 中存在类型推断机制,在没有指定类型的情况下,TS 也会给变量提供类型。
- typeof 后面接的是值 返回的是值的类型
- keyof 后面接的是类型 返回的也是类型 或者说 返回的是类型中的键组合的字面量联合类型
// 类型推断: typeof 后面接值 ====> 返回的是 类型
const obj4 = {
id: 1,
name: 'jack'
}
type Tperson4 = typeof obj4 // 进行类型推断
let obj5 = {} as Tperson4 // 结合as类型断言
obj5.id = 2
obj.name = 'rose'
// keyof 后面接的是类型 返回的也是类型
const res = {
info: 'admin',
introduce: '数据',
mobile: 13020203030
}
type Tt = typeof res
type key = keyof Tt // 返回的是 "info" | "introduce" | "mobile" 返回的是类型中的键 组成的字面量联合类型
十五 泛型
- 在TypeScript中,泛型是一种创建可复用代码组件的工具。这种组件不只能被一种类型使用,而是能被多种类型复用。类似于参数的作用,泛型是一种用以增强类型(types)、接口(interfaces)、函数类型等能力的非常可靠的手段。
泛型别名
// 泛型别名
type user = {
id: number
age: number
}
type Goods = {
hobby: string[]
gender: string
}
type Data<T> = {
id: number
get: string
res: T
}
type userInfo = Data<user>
/* 返回的结果如下
{
id:number
get:string
res: {
id: number
age: number
}
}
*/
type userInfo1 = Data<Goods>
/* 返回的结果如下
{
id:number
get:string
res: {
hobby: string[]
gender: string
}
}
*/
type Data1<T, S> = {
id: number
get: string
res: T
code: S
}
type userInfo3 = Data1<Goods, user>
泛型接口
// 泛型接口 没有等号=
interface Iperson1<T, S = string> {
id: number
code: number
messgae: S
data: T
}
type Iperson3 = Iperson1<number>
// 验证类型Iperson3
const obj6: Iperson3 = {
id: 1,
code: 2,
messgae: '456',
data: 789,
}
泛型函数
// 泛型函数
// 函数名称后加上 <Q> , Q是类型参数,是个类型变量
// 当你调用函数的时候,传入具体的类型,Q 或捕获到这个类型,函数任何位置均可使用。
type Q = number
function fn1<Q>(a: Q): Q {
return a
}
// add1<Q>('123') //会提示不能赋值string
fn1('123') //会自动进行类型推断
// 泛型函数继承
type F3 = string
function fn2<Q extends F3>(a: Q): Q {
return a
}
fn2('1')
// 泛型函数 继承 类型推断
const c1 = { id: 1 }
function fn3<Q extends typeof c1>(a: Q): Q {
return a
}
fn3({ id: 2 })
// 泛型函数 继承 keyof
type c2 = { id: 1 } //keyof c2 返回c2类型的键 、Q extends keyof c2 Q继承c2返回的类型中的键 也就是字面量id
function fn4<Q extends keyof c2>(a: Q): Q {
return a
}
fn4('id') //参数 只能输入'id'
枚举
// 枚举 enum
// 可以设定初始值
enum Day {
sunday,
monday,
tuesday,
wednesday,
thursday,
friday,
saturday
}
function fn5(Day: Day) {
return Day
}
fn5(Day.sunday)
TypeScript 应用
前提:script 加上
lang="ts"
才能写ts代码<script setup lang="ts"></script>
defineProps的TS写法
- defineProps 的基本使用:
父传:
<script setup lang='ts'>
// 父传
import son from './components/son.vue'
const sum = 100
</script>
<template>
<div>
<div>我是父组件</div>
<hr />
<son :money="sum" :car="'宝马x5'" num="你好"></son>
</div>
</template>
<style lang='less' scoped></style>
子收:
<script setup lang='ts'>
//1 v3的写法
// defineProps({
// money: Number,
// car: {
// type:String
// },
// num: String
// })
//2 ts写法 没有默认值的写法: 泛型的写法
// defineProps<{
// money: number
// num: string
// car?: string //可选参数
// }>()
// 3 ts写法 有默认值的写法:withDefaults(defineProps<>(),{默认值})
// 父组件没传car的情况下就显示默认值 ,父组件传值了显示传的值
// withDefaults(defineProps<{
// money: number
// num: string
// car?: string //可选参数
// }>(), {
// car: '凯迪拉克'
// })
// 4 有默认值的简易写法 父组件没传car的情况下就显示默认值 car = '凯迪拉克'
let { money, num, car = '凯迪拉克' } = defineProps<{
money: number
num: string
car?: string
}>()
</script>
<template>
<div>
===vue3
===={{ money }}==={{ num }}==={{ car }}
<div>我是子组件son</div>
</div>
</template>
<style lang='less' scoped>
</style>
defineEmits的TS写法
- defineEmits 的基本用法:
vue3写法:
const emit = defineEmits(['事件名'])
2.defineEmits 通过泛型参数来定义,可以实现更细粒度的校验:
ts写法:
ts 泛型 const emits = defineEmits<>()
const emits = defineEmits<{
(e: 'handel', s: string): void
(e: 'handel2', s: number): void
}>()
子传:
子传
<script setup lang='ts'>
// 第一种子传父 vue3
// const emits = defineEmits(['handel'])
// const btn = () => {
// emits('handel', '我是子组件son传的值')
// }
// 第二种子传父 ts泛型 const emits = defineEmits<>()
const emits = defineEmits<{
(e: 'handel', s: string): void
(e: 'handel2', s: number): void
}>()
const btn = () => {
// 子向父传值
emits('handel', '我是子组件son传的第一个值')
}
const btn2 = () => {
// 子向父传值
emits('handel2', 123456)
}
</script>
<template>
<div>
<div>我是children子组件</div>
<button @click="btn">子传父</button>
<button @click="btn2">子传父2</button>
</div>
</template>
<style lang='less' scoped>
</style>
父收
<script setup lang='ts'>
// 父收
import children from './components/children.vue'
// 第一种方法
// const msg = () => {
// console.log('handel')
// }
// 第二种方法:
import { ref } from 'vue'
const msg = ref('')
const msg2 = ref(1)
</script>
<template>
<div>
御剑乘风来,除魔天地间!===vue3
<div>我是父组件</div>
<hr />
<!-- <children @handel="msg"></children> 第一种方法-->
<!-- 子向父传值 第二种方法-->
===={{ msg }}
===={{ msg2 }}
<children @handel="msg = $event" @handel2="msg2 = 123456"></children>
</div>
</template>
<style lang='less' scoped>
</style>
ref的TS写法 + ref获取dom元素
ref()
会隐式的依据数据推导类型
- 如果是简单类型,推荐使用类型推导:
- 如果是复杂类型,推荐指定泛型:复杂数据一般是后台返回数据,默认值是空,无法进行类型推导。
<script setup lang='ts'>
import { ref } from "vue";
type T = {
id: number
name: string
}
let list = ref<T[ ]>([ ]) //<T[ ]> 表示一个list这个数组里面的每一个数据都是T这样的对象类型{id:123,name:"jack"}
list.value.push({ id: 123, name: 'rose' }) //
</script>
<template>
<div>
===vue3
</div>
</template>
<style lang='less' scoped>
</style>
reactive的TS写法
reactive()
也会隐式的依据数据推导类型
- 默认值属性是固定的,推荐使用类型推导:
- 根据默认值推导不出我们需要的类型,推荐使用接口或者类型别名给变量指定类型:
推荐使用ref( )
computed和TS
computed()
会从其计算函数的返回值上推导出类型:- 可以通过泛型参数显式指定类型:
<script setup lang='ts'>
import { ref, computed } from "vue";
type T = {
id: number
name: string
}
let list = ref<T[]>([]) //<T[]> 表示一个list这个数组里面的每一个数据都是T这样的对象类型{id:123,name:"jack"}
list.value.push({ id: 123, name: 'rose' }) //
// const newList = computed(() => { //vue3写法
// return list.value
// })
const newList = computed<T[]>(() => { //泛型写法 T[]返回值的类型
return list.value
})
let num = ref(100)
const newNum = computed<string>(() => {
return (num.value * 10).toFixed(2) //保留2位小数
})
</script>
<template>
<div>
御剑乘风来,除魔天地间!===vue3
===={{ newList }}==={{ newNum }}
</div>
</template>
<style lang='less' scoped>
</style>
事件处理与TS
<script setup lang='ts'>
import { ref, onMounted } from "vue"
// 事件对象 ===ts
const handelChange = (e: Event) => {
// 类型断言
let val = (e.target as HTMLInputElement).value
console.log(val, 99);
}
// ref获取dom元素
const dom = ref<HTMLInputElement | null>(null)
onMounted(() => {
// console.log(dom.value?.value,77);
// if(dom.value){
// console.log(dom.value.value,88);
// }
// ts非空断言
console.log(dom.value!.value, 88);
})
</script>
<template>
<div>
御剑乘风来,除魔天地间!===vue3
<input ref="dom" type="text" @change="handelChange" value="默认值" />
</div>
</template>
<style lang='less' scoped>
</style>
TypeScript类型声明文件
项目中安装的第三方库里面都是打包后的JS代码,但是我们使用的时候却有对应的TS类型提示,这是为什么呢?
- 在第三方库中的JS代码都有对应的
TS类型声明文件
什么是类型什么文件?
- 通俗地来讲,在 TypeScript 中以 .d.ts 为后缀的文件,我们称之为 TypeScript 类型声明文件。它的主要作用是描述 JavaScript 模块内所有导出成员的类型信息。
TS 中有两种文件类型:.ts
文件 .d.ts
文件作用是啥?
- .ts 文件:
既包含类型信息又可执行代码
- 可以被编译为 .js 文件,然后,执行代码
- 用途:编写程序代码的地方
- .d.ts 文件:
只包含类型信息
的类型声明文件- 不会生成 .js 文件,仅用于提供类型信息,在.d.ts文件中不允许出现可执行的代码,只用于提供类型
- 用途:为 JS 提供类型信息
小结:
- .ts 是
implementation
代码实现文件 - .d.ts 是
declaration
类型声明文件 - 如果要为 JS 库或者模块提供类型,就需要类型声明文件
第三方库类型声明文件
给第三方库添加对应的类型声明文件
首先,常用的第三方库都有相应的类型声明文件,只是使用的方式不同而已。
情况1:库本身自带类型声明文件
- 比如:axios,安装后可查看
node_modules/axios
可发现对应的类型声明文件。 - 导入 axios 后就会加载对应的类型文件,提供该库的类型声明。
情况2:由 DefinitelyTyped 提供
- 比如:jquery,安装后导入,提示:需要安装
@types/jquery
类型声明包 - (pnpm i
@types/jquery
) - DefinitelyTyped 是一个 github 仓库,用来提供高质量 TypeScript 类型声明
- 当安装
@types/*
类型声明包后,TS 也会自动加载该类声明包,以提供该库的类型声明
自定义类型声明文件 共享类型
使用类型声明文件提供需要共享的TS类型
- 如果多个
.ts
文件中都用到同一个类型,此时可以创建.d.ts
文件提供该类型,实现类型共享。 - 操作步骤:
- 创建
index.d.ts
类型声明文件。 - 创建需要共享的类型,并使用
export
导出(TS 中的类型也可以使用import/export
实现模块化功能)。 - 在需要使用共享类型的
.ts
文件中,通过import
导入即可(.d.ts
后缀导入时,直接省略)。
- 创建