-----------------------------------------------------------------------------
ts
typescript 是 js的超集
微软开源的
vscode也是微软开源的
js是弱类型的
js大多是错误都是类型错误
如var num = 10; num = 'test' 数据从一个类型改为另一个类型
上面的代码调用 num.tofixed(2)就会报错 “tofiexd not a function”因为字符串没有这个方法
ts属于静态类型的编程语言 js属于动态的编程语言
静态类型:编译期做类型检查
动态类型:执行时做类型检查
react:ts + hooks
vue:vue3 + ts
vue2对ts的支持不友好
写ts代码 用tsc转为js代码 浏览器只认识js代码
实际开发中是webpack 或者 vite来做
yarn create vite 项目名 --template vue-ts
ts
类型注解
:number就是一种
为变量添加类型约束
约定了什么类型就只能赋值什么类型 否则就会报错
约定了类型后代码的提示就会很清晰
比如let num: number = 18
number. 就会点出来所有能用的方法 比如tofixed等等
写代码的时候出错了就会报错(也就是编译时报错)
js已有简单类型
null、undefined、boolean、number、boolean
复杂类型
对象、数组、函数等
ts新增类型
联合类型
自定义类型
接口
元组
字面量类型
枚举
void
多个.ts存在 如果在ts文件内不写export {} 而且有相同变量就会报错 。 因为只有加了之后才会被看做是模块 不写就相当于 是全局变量 相同的全局变量名只能有一个
ts中数组的写法
数字数组 不能写undefined给数组
let numArr: number[] = [1, 2, 3]
字符串的两种写法
let strArr: String[] = ['q', 'w', 'e']
// 泛型
let strArr2: Array<String> = ['q', 'w', 'e']
联合类型
数组中有两个或者多个类型的数据
// num可以使数字或者字符串
let num: number | string = '18'
// 数组中可以有字符串和数字
let arr: (number | String)[] = ['123',4,5,6]
// 定时器id赋初始值 null 或者 -1
let timerId: number | null = null
// let timerId: number | null = -1
// let timerId: number = -1
timerId =setTimeout(() => {
}, 1000);
自定义类型
type CustomArray = (number | string)[]
let arr: CustomArray = ['123',4,5,6]
// 定义函数提前要给参数和返回值定义类型
function add(n1: number, n2: number):number {
return n1 + n2
}
console.log(add(1,2));
// 指定整个函数的类型 给函数表达式用的 方便函数复用
type MyFn = (n1: number, n2: number) => number
const add: MyFn = function (n, n2){
return n+n2
}
const sub: MyFn = function (n, n2){
return n-n2
}
函数没有返回值用:void
如果函数返回值是undefined 要用:undefined
function greet(name:string): void{
console.log(name+"hello");
}
可选表达式 给形参加 ? 说明这个参数可传可不传 不传的话是undefined
function add(n1: number, n2?: number): void{
console.log(n1,n2);
}
add(1,2)
-----------------------------------------------------------------------------
对象类型
// 对象的几种写法
// 写法一
let obj: {} = {}
// 写法二 不换行的话类型注解要写 ;(分号)
let obj2: { name: string; age: number } = {
name: 'zs',
age:18
}
// 写法三 换行的话类型注解可以不写 ;(分号)
let obj3: {
name: string
age: number
} = {
name: 'ls',
age:20
}
对象中的方法的两种写法
普通写法
let obj: {
say(): void
add(n1: number, n2:number):number
} = {
say() {
console.log('hello, world');
},
add(n1, n2) {
return n1 + n2
}
}
箭头函数写法
let obj4: {
say: () => void
add: (n1: number, n2: number) => number
} = {
say() {
console.log('hello, world');
},
add(n1, n2) {
return n1 + n2
}
}
接口类型
接口以interface关键字声明
接口名称推荐以I(大写I)开头 如IPerson
interface IDog {
name: string
age: number
type: string
bark: () => void
}
const retriever: IDog = {
name: '小金毛',
age: 1,
type: '金毛',
bark() {
console.log('wang wang wang');
}
}
type 声明后接 =
type MyFn = (n1: number, n2: number) => number
interface 声明后直接接 {} 且不用加;(分号)
interface IDog {
name: string
age: number
type: string
bark: () => void
}
相同:都可以给对象指定类型
不同:type可以给函数指定类型别名 基本类型等 , interface只能给对象指定类型
type单词更短更简单 而且type语法更加的新 能用type就用type
接口继承
interface IPoint2D {
x: number
y: number
}
interface IPoint3D extends IPoint2D {
// 这里会继承IPoint2D 的x y
z: number
}
元祖tuple
比数组更加严谨
let num: number[] = [1,2,3,4,5]
此时num中可以有任意个数字
元祖就不一样了
let num: [number, number] = [118.111, 36]
[]中写了几个就只能赋值几个数
类型推论
某些没有明确指出类型的地方 ts的类型推论机制会帮助提供类型
2中常见场景
1.声明变量并初始化时
2.决定函数返回值时
类型推论只在声明的时候会做
如下的num就会被推论为number
let num = 18
num = 'str' // 这里报错number不可以被赋值为string类型
如下的a被推论为any所以可以这么写
let a
a = 'any'
a = 18
函数返回值也有推论 会自动根据return后的语句推论类型
如下被推论为number
function add(a: number, b: number) {
return a + b
}
但是指定返回值后就不可以return别的类型 否则会报错
开发时能省略类型注解的地方就省略(充分利用ts的类型推论能力)
根据公司要求来 严格就无论怎样都要写 不严格就可以偷懒不写
如果不知道类型可以把鼠标放到变量名称上 利用vscode提示来查看类型
字面量类型
// 类型注解为 string
let str = "Hello Ts"
// 类型注解为 Hello Ts 值只能为 Hello Ts
const str2 = "Hello Ts"
// 作用类似上面的const 类型注解为 Hello Ts 值只能为 Hello Ts
let str3:"Hello Ts" = "Hello Ts"
// 配合联合类型使用 类型注解为 '男' | '女' 值只能为 男 女
let gender: '男' | '女' = "男"
枚举类型
枚举要用到值的场景推荐用枚举 如果枚举当做类型来用的时候不推荐 推荐用type
enum
enum Direction { up,down,left,right }
function move(direction: Direction): void{
console.log(direction);
// up,down,left,right 分别对应0 1 2 3
}
move(Direction.down)
ts的类型在编译成js的时候 所有ts的类型会被擦除 ts的类型(type)不能用来当成值赋值使用
枚举不仅是可以当类型 还可以当值使用
type A = string
let a = A // A仅表示类型这里却作为值使用
数字枚举和应用
如果枚举中的属性赋了初始值下面的值都会在上一个的基础上+1
enum Direction {
up = 10,
down,
left,
right
}
function move(direction: Direction): void{
console.log(direction);
// up,down,left,right 分别对应10 11 12 13
}
move(Direction.up)
move(Direction.down)
move(Direction.left)
move(Direction.right)
也可以给每一个enum赋初始值
enum Direction {
up = 10,
down = 12,
left = 18,
right = 20
}
// enum用于性别的时候
// 男0 女1
enum Gender {
mail,
femail
}
function fn(gender: Gender):void {
console.log(gender);
}
fn(Gender.mail)
fn(Gender.femail)
// 而不要写成下面这样 下面的0 1称为魔术数字 别人不知道0 和 1代表什么意思
// fn(0)
// fn(1)
字符串枚举
使用要注意一旦给某个值赋了字符串值就要给所有的值赋字符串值 因为字符串无法自增 而枚举类型不赋值 下一个就会等于上一个值+1
enum Direction {
up = "上",
down = "下",
left = "左",
right = "右",
}
enum Gender {
男 ,
女
}
console.log(Gender.男);// 0
console.log(Gender.女);// 1
console.log(Gender[0]);// 男
console.log(Gender[1]);// 女
any类型
不推荐使用any
any
声明变量不给类型和默认值
函数参数不给类型
any用多了代码就会变成“anyScript”
类型断言
我比ts更清楚这个数据类型是什么 所以加上断言 给它一个更加具体的类型
as关键字 实现断言
例如a链接 获取dom元素的时候ts认为你获取的是HTMLElement 此时无法访问.herf 但是a链接实际是 HTMLAnchorElement
img获取dom元素的时候ts认为你获取的是HTMLElement 此时无法访问.src 但是img实际HTMLImageElement
导致不准确 但是我知道它是什么类型 而且需要访问该类型的属性 所以需要类型断言
let link = document.getElementById('link') as HTMLAnchorElement 就可以link.href了
let img = document.getElementById('img') as HTMLImageElement 就可以img.src了
断言另一种语法
let link = <HTMLAnchorElement>document.getElementById('link')
type User = {
name: string
age: number
gender: string
}
// 这里不as User 就不能赋值空对象 下面也无法user.出name age gender 也无法修改name age gender
let user: User = {} as User
setTimeout(() => {
user = {
name: 'zs',
age: 18,
gender: '男'
}
}, 1000);
泛型
让函数、接口、class 复用
常见泛型:函数 接口 class泛型
T相当于类型变量 相当于一个容器
// 定义的函数带泛型 传什么类型 返回什么类型
function fn<T>(value: T): T{
return value
}
// 调用的时候也要带有泛型
const res = fn<number>(1)
const str = fn<string>('str')
console.log(res,str);
泛型函数简化使用
可以隐式识别类型 可以省略<类型> ts内部有类型推断机制
编译器推断不准确或者无法推断的时候需要传入类型参数
let res = fn(1)
let str = fn('str')
泛型约束
// 要添加的约束 为了保证所有传入的值都可以点出一个或多个属性 就要加泛型约束
interface ILength { length: number }
// 不加约束的话 不知道value有没有.length 不一定能点出length所以会报错 加了之后一定可以.出length
function fn<Type extends ILength>(value: Type): Type{
console.log(value.length);
return value
}
type User = {
name: string
age: number
}
function showUser<T extends User>(user:T):void {
console.log(user.name);
console.log(user.age);
}
泛型可以有多个类型变量
keyof关键字
type User = {
name: string
age: number
}
// 这里的A就是name或者age
const A = keyof User
keyof Type 可以获取Type中的所有键
function fn2<Type, Key extends keyof Type>(obj: Type, key: Key):void {
console.log(obj[key]);
}
泛型接口
接口中所有成员都可以使用类型变量
使用泛型接口时 需要指定具体的类型
interface User<T> {
name: T
age: number
sayHi(): T
}
let num: number[] = [1, 2, 3]
let num2: Array<number> = [1, 2, 3]
let num3: Array<number> = [1, 2, 3]
之后push也只能push number类型的数据
ts 配合defineProps使用泛型
<script lang="ts" setup>
// const props = defineProps({
// money: {
// type: Number,
// required: true
// },
// car: {
// type: String,
// required: true
// }
// })
const props = defineProps<{
money: number
car?: string
}>()
</script>
car可以传可以不传
或者写成下面的样子
interface Props {
money: number
car?: string
}
https://vuejs.org/guide/extras/reactivity-transform.html#explicit-opt-in
这个网址有vite和webpack分别怎么配置 指定默认值的配置
const props = defineProps<Props>()
// 指定默认值
const { money, car = '小黄车' } = defineProps<Props>()
提供默认值car
car要想响应式要在vite.config.ts配置
export default defineConfig({
plugins: [vue({
reactivityTransform: true
})]
})
ts 配合 defineEmits
defineEmits<
const emit = defineEmits<{
(e: 'changeMoney', money: number): void
(e: 'changeCar', car: string): void
}>()
const changeMoney = function () {
emit('changeMoney', 10)
}
ts 配合 ref
如果ref是简单数据类型可以简写
如果ref是复杂类型 可能会推断不出类型 就需要指定泛型
let money = ref<number>(100) 等同于 let money = ref(100)
也可以写成
let money:Ref<number> = ref(100)
// 指定空数组 或者空对象 就要给泛型
type Todos = {
id: string
name: string
done: boolean
}[]
const list = ref<Todos>([])
list.value = [{ id: 1, name: 'asd', done: false }]
ts 配合reactive
reactive 也是一样 空数组或者空对象就要给泛型 基本类型或者不是空数组空对象就可以给泛型
ts 配合 computed
会根据计算属性的返回值 推断类型
也可以写泛型
const money = ref(10)
const double = computed<number>(()=>{
return money.value*2
})
-----------------------------------------------------------------------------
// 如何查看事件对象的类型:技巧:查看$event的类型即可
鼠标悬浮到$event 就可以看到事件对象了
<h1 @mousemove="move($event)">我是一个标题</h1>
不知道dom类型时 怎么查看dom类型
可以document.createElement('h1').__proto__ // HTMLHeadingElement
provide和inject可以看网址 不常用 常用pinia
https://vuejs.org/guide/extras/reactivity-transform.html#explicit-opt-in
可选链操作符?.(问号 点)用于获取数据
会判断前?.前面的属性(或方法)是不是null 或者 undefined 是的话就返回undefined 不是的话就返回?.后面的值
const user = {
name: 'zs',
cat: {
name: '布偶',
},
}
// user.cat?.name?.anchor?.bind?.c
// user.cat?.['name']
// user.arr?.[0]
// user.sayHi?.() // 判断这个函数有没有有就调用
console.log(user.cat?.['name'])
非空断言 !感叹号
非空断言一定要保证该数据为非空
const imgRef = ref<HTMLImageElement | null>(null)
const imgUrl = imageRef.value!.src
ts的类型声明文件
ts有两种文件类型 .ts .d.ts
.d.ts文件是类型提示(比如数组类型数据会提示有.push .pop等数组相关的方法)相关的文件
内置类型声明文件
ts为js运行时可用的所有标准化内置API都提供了声明文件 写个.就有提示
axios 也有.d.ts文件 外部导入的插件有的会自带.d.ts文件 写代码就会有提示了
jquery 这种老的库 就没有.d.ts文件
https://github.com/DefinitelyTyped/DefinitelyTyped/
帮市面上主流的库都添加了.d.ts文件
yarn add @types/包名 就可以了
例如:@types/react @types/loadsh
自定义类型声明文件 .d.ts
.d.ts文件中不可以写js代码 只能写声明语句 加上declare 就是声明的意思
/// 三斜线表达式(ts独有的)
跟<script>很像
.d.ts文件
提供共享数据
在types文件夹下建一个data.d.ts
export type Todos = {
id: number
name: string
done: boolean
}[]
提供类型声明
使用场景“”
老项目js迁移为ts 跟js同级建一个.d.ts
成为一名库开发者
utils文件夹下建index.d.ts
在main.js 中导入的是js TS会自动加载与.js同名的.d.ts文件 以提供类型声明文件
在.d.ts中使用declare 为js中已存在变量声明类型
type interface等只在ts出现的可以不加declare
let function在js和ts都出现的 要加declare
官方文档提供了一个页面,可以来查询 @types/* 库
https://www.typescriptlang.org/dt/
axios可以指定泛型的方法
axios.request() 等价于 axios()
axios.get
axios.post
axios.put
axios.delete
// 单行注释
/*
多行注释
多行注释
多行注释
多行注释
*/
/**
*文档注释
*/
用vite创建的仓库 默认没有帮你创建git仓库