TypeScript
1.TS 类型
1.类型级别
// any 任意类型 unknown 类型
// 1.top type 顶级类型 any unknown
// 2.Object
// 3.Number String Boolean
// 4.number string boolean
// 5. 1 "1" false
// 6.never
2.any类型和unknown类型
//1.any和unknow类型的值可以为任何类型
//2.unknown只能赋值给自身,或者是any类型
//3.unknown 自身的属性无法读取
//总结 unknown 类型比 any 类型更加安全
let any:any = "666"
let unk:unknown = "666"
let num:number = 999
// 同为顶级类型,unknown只能赋值给自身,或者是any类型
// num = nuk //报错
num = any
console.log(num);
//unknown 自身的属性无法读取
let unkobj:unknown = {
open:()=>{
console.log("open");
}
}
console.log(unkobj.open()); //报错
3.Objcet原始类型、object非原始类型 和 { }类型
// Object 原始类型
let a:Object = 123
let a1:Object = "123"
let a2:Object = []
let a3:Object = {}
let a4:Object = true
let a5:Object = ()=>123
// object 非原始类型
let b:object = 123 //错误 number非原始类型
let b1:object = "123" //错误 string非原始类型
let b2:object = []
let b3:object = {}
let b4:object = true //错误 boolean非原始类型
let b5:object = ()=>123
// {}对象类型类似于Object
let c:{} = 123
let c1:{} = "123"
let c2:{} = []
let c3:{} = {}
let c4:{} = true
let c5:{} = ()=>123
4.Interface接口与对象类型
// 1.同名:使用interface属性需要和interface的参数完全一致
// 2.重合:添加同名接口,新增其它字段。使用该接口的变量,要完全实现接口里所有字段
// 3.索引签名:索引签名的内容不在进行强校验,但是接口里其它字段需要完全实现
// 4.?与readonly:?修饰的字段可以不用实现。readonly修饰的字段不可以被重写
// 5.interface继承: interface A extends B A继承B接口,实现类需要实现接口A和接口B的所有字段
// 6.interface定义函数类型:见代码第57条 23333QAQ
// 继承B接口,实现类需要实现接口A和接口B的所有字段
interface A extends B{
name:string
age:number
// 索引签名,类型最好是any。如果是string,其它字段类型都必须为string
[prop:string]:any
// ?可选修饰符,可以不用实现
phone?:string
// readonly修饰的字段不可以被重写
readonly cb:()=>boolean
}
// 重合:添加同名接口,新增参数。使用该接口的变量要完全实现接口里所有参
interface A{
sex:string
}
//接口 B
interface B {
bbb:string
}
// 属性需要和interface完全一致
let a:A = {
name:"luxiaohang",
age:24,
sex:'男',
// 使用索引签名
a:1,
b:2,
// readonly方法被实现后,不可以再次修改
cb:()=>{
return true
},
// 实现继承接口B的字段
bbb:"我是继承B接口的属性"
}
console.log(a.cb());//true
// 修改readonly修饰符修饰的方法
//a.cb = ()=>{} //报错
//6.定义函数类型
interface Fn{
(name:string):number[]
}
const fn:Fn = (name:string)=>{
return [1,2,3]
}
5.数组类型
{
//1.number[]
//2.泛型 Array<boolean>
//3.数组普通类型
//4.数组实现接口,创建对象数组
//5.多维数组类型:代码 17 行二维数组 qwq jvi
//6.函数中数组类型用法,以及arguments
// 数组实现接口
interface Arr{
name:string
age?:number
}
// 对象数组
const arr:Arr[] = [{name:"zhangsan"},{name:"lisi",age:18}]
// 多维数组
let arr2:number[][][] = [[],[],[]]
// 多种类型
let arr3:Array<Array<number|string|boolean>> = [[1],["2"],[true]]
// arguments
function fn(...arg:number[]){
let a:number[] = arg
console.log(a);
let b:IArguments = arguments
console.log(b);
}
fn(1,2,3)
}
6.函数类型
// 1.函数定义类型和返回值 | 箭头函数定义类型和返回值
// 2.函数默认的参数 | 函数可选参数
// 3.函数是一个对象如何定义
// 4.函数this类型
// 5.函数重载
// 定义函数类型
function add(a:number=10,b:number=20) :number{
return a+b
}
// 定义箭头函数类型以及返回值
const multiply =(a:number,b:number):number => {
return a * b
}
const result = multiply(4,5)
console.log(result);
//2.可选参数以及默认参数
function defaultArg(a:number = 10,b?:number):number {
if (b) {
return a+b
}
return a
}
// 3.函数是一个对象如何定义
// 函数的对象参数
interface Obj{
name:string
age:number
}
// 给函数传一个对象,这里接收一个Obj类型的对象
function fn(obj:Obj):object {
return obj
}
// 4.函数this类型
interface Obj1{
user:number[]
add:(this:Obj1,num:number)=>void
}
// ts 可以定义this 的类型 在js中无法使用 必须是第一个参数定义 this 的类型
let obj:Obj1 = {
user:[18,66],
add(this:Obj1,num:number) {
this.user.push(num)
},
}
// 5.函数重载
const arr = [1,2,3,4]
function put(arr:number):number[]
function put(arr:number[]):number[]
function put():number[]
function put(num?:number | number[]) {
if (typeof num == "number") {
// 返回arr等于传进put参数的值
return arr.filter(item=>item == num)
}else if(Array.isArray(num)){
// 返回合并后的数组
arr.push(...num)
return arr
}else{
return arr
}
}
console.log(put());
console.log(put(2));
console.log(put([9,8,7,6]))
7.联合类型 | 交叉类型 | 类型断言
// 1.联合类型 | 满足一种类型即可
// 2.交叉类型 & 需要同时满足所有类型
// 3.类型断言 as 将类型断言成某种类型,供编译器使用。可能会欺骗编译器
// 联合类型
let a:string | number = 123456
a = '123456'
const fn = (type:number | boolean):boolean=>{
// 双感叹号转换为布尔类型
return !!type
}
console.log(fn(1))
// 交叉类型
interface People{
name:string,
age:number
}
interface Man{
sex:number
}
const lxh = (man:People & Man)=>{
console.log(man);
}
lxh({
name:"luxiaohang",
age:18,
sex:1
})
// 3.类型断言
let fnAs = (num:number|string)=>{
// num可能为number类型自身没有lenth方法,所以需要类型断言
console.log((num as string).length);
}
fnAs(123456)
// 类型断言可能出现的问题
interface A{
a:number
}
interface B{
b:string
}
function asAB(num:A | B):void {
// 通过泛型类型断言
console.log((<B>num).b );
}
// 传入一个不能被断言识别的类型时,可能会得到undefine的结果
asAB({a:123})
8.内置对象
// 内置对象
// 1.ecma模块 Date RegExp Error XMLHttpRequest
// 2.dom模块 querySelect
// 3.bom模块
// ecma模块
let num:Number = new Number(1)
let date:Date = new Date()
let rep:RegExp = new RegExp(/\w/)
let error:Error = new Error("err")
let xhr:XMLHttpRequest = new XMLHttpRequest()
// dom模块 HTML(元素名称) Element HTMLElement Element
let inp:HTMLInputElement = document.querySelector('input') as HTMLInputElement
// 类型不固定可以给到节点类型,往里面传入其它HTML类型
let div:NodeListOf<HTMLDivElement | HTMLElement> = document.querySelectorAll('div')
// Bom模块
let local:Storage = localStorage
let lo:Location = location
// document.cookie 返回值是字符串
let cookie:string =document.cookie
// promise类型 返回值promise
let promise:Promise<number> = new Promise((res,rej)=>rej(1))
promise.then((res)=>{
console.log("success",res);
})
.catch((rej)=>{
console.log("error",rej);
})
9.class 类
1. class类型约束 implement和继承
// 1.class 用法和类型约束 implement
// 2.继承
interface Options {
el: string | HTMLElement;
}
interface Vueclass {
options: Options;
init(): void;
}
interface Vnode {
tag: string; //div
text?: string; //虚拟dom的key
children?: Vnode[]; //虚拟dom集合
}
// 虚拟 dom
class Dom {
// 创建节点方法
createElement(el: string) {
return document.createElement(el);
}
// 填充文本 text可能为空
setText(el: HTMLElement, text: string | null) {
el.textContent = text;
}
// 渲染函数
render(data: Vnode) {
// 渲染第一个dom
let dom = this.createElement(data.tag);
if (data.children && Array.isArray(data.children)) {
// 如果children存在子节点,则递归创建dom
data.children.forEach((item) => {
let child = this.render(item);
dom.appendChild(child);
});
} else {
// 如果没有子节点了,直接给dom添加文本
this.setText(dom, data.text as string);
}
return dom;
}
}
// vue 实现了 Vueclass 接口 ,并且继承了虚拟dom方法
class vue extends Dom implements Vueclass {
options: Options;
// 传进来的一个对象 包含el
constructor(element: Options) {
super();
this.options = element;
this.init();
}
init(): void {
// 虚拟dom 就是通过js 去渲染真实dom
let data: Vnode = {
tag: "div",
children: [
{
tag: "section",
text: "我是子节点1",
},
{
tag: "section",
text: "我是子节点2",
children: [
{
tag: "section",
text: "我是子节点2-1",
},
],
},
{
tag: "section",
text: "我是子节点3",
},
],
};
// 如果是string则手动获取dom节点,不是string而是dom直接使用该dom
let app =
typeof this.options.el == "string"
? document.querySelector(this.options.el)
: this.options.el;
// 渲染一个dom
app?.appendChild(this.render(data));
}
}
new vue({
el: "#div",
});
2.class 修饰符 readonly public
// 1.class 成员属性 readonly public protected private
// 成员属性默认为 public 可以给子类、外部去调用
// 2.super() 原理是父类的 prototype.constructor.call
// 可以给父类的constructor中传参
// 3.静态方法 static 外部可以不用实例化类,直接调用类成员方法
// static 方法内只能使用static修饰的方法和属性。无法使用类中其它方法和属性
// 4. get() set()
class A {
readonly red:number = 1;
constructor(str?:string){
// 只能从在内部去调用pri
// this.pri()
// 打印实例化传进来的参数
console.log(str);
}
protected prot(){
console.log("我是被protected修饰的内部方法");
}
private pri(){
console.log("我是被private修饰的内部方法");
}
}
const a = new A()
// a.red = 2 // 报错 readonly 修饰的成员属性无法被修改
// a.print() //报错 private修饰的成员属性,无法被外部和子类调用
// a.prot() //报错 protected修饰的成员属性,只能在类中和子类去调用
// 2.super()
class AA extends A{
a = 6
static b = "123"
constructor(){
// 调用父类构造器
super("我是子类参数,调用了父类构造器")
}
prot(){
// 可以通过super.属性名 直接使用父类属性或方法
super.prot()
}
//3.static
static printa(){
// static修饰方法无法调用非 static修饰成员
// let a = this.a //报错
// let b = this.b
console.log('a');
}
}
const aa = new AA()
AA.printa()
// 4. get()/set()
class getset{
// 私有成员属性,通过get\set方法暴露出去,可以更好的保护属性安全
private _a:any
get a(){
return this._a
}
set a(a:any){
this._a = a
}
}
const result = new getset()
// 通过get a获取私有变量 _a 的值
console.log("get a =", result.a)
// 通过set a修改私有变量 _a 的值
console.log("set a =", result.a = 999)
// 通过get a获取私有变量 _a 的值
console.log("get a =", result.a)
3.abstract抽象类
// 抽象类
// abstract 所定义的是抽象类,抽象类中可以有抽象方法也可以有普通方法
// 抽象类不能被实例化
// 抽象类的抽象成员属性必须被实现
abstract class Animal{
name?:string
abstract behavior:any
constructor(name?:string,behavior?:string){
this.name = name
}
getName():string | undefined{
return this.name
}
abstract init(name:string):void
}
class Cat extends Animal {
behavior:string
constructor(){
super()
this.behavior = "小猫喵喵叫"
}
//必须实现父类的抽象方法
init(name:string){
}
setName(name:string){
this.name = name
}
}
const cat = new Cat()
cat.setName("张三")
//继承抽象类后,可以直接调用父类非抽象方法
console.log(cat.getName()) //打印: 张三
console.log(cat);
console.log(Cat);
10.tuples 元组
//1.元组类型
//2.元组联合类型
//3.获取元组类型
// 元组类型
let arr:[number,boolean] = [1,true]
arr.push(6)
// 将元组设置为完全只读,不可修改其数值和存储地址
let arr2:readonly[number,boolean] = [2,false]
arr2 = [9,false]
const arr3 = arr2
// arr3 = [14,false] //报错
// arr3.push(666) //报错
// 元组结合联合类型
const arr4:readonly [x:number,y?:boolean | string] = [1,"123"]
console.log(arr,arr2);
// 获取元组类型
type tuplesType = typeof arr4[0]
type tuplesType2 = typeof arr4[1]
type tuplesType3 = typeof arr4["length"]
11.枚举enum
// 枚举
// 普通函数区分颜色
const fn = (type:any)=>{
if(type ==0){
return "red"
}
if(type == 1){
return "green"
}
}
console.log(fn(0))
// 使用枚举
enum Color{
red = 0,
green= 1,
}
class opColor{
constructor(color:any){
// 枚举反向映射
if (color == Color.red) {
console.log(`key = ${Color[color]}`,`value = ${Color.red}` ) // 输出 key = red,value = 0
}
if (color == Color.green) {
console.log(Color[color])
}
}
}
let color = new opColor(1)
12.类型推断 | 类型别名 Type
// 1.类型别名 type
// 2.type 和 interface 区别
// 3.type 联合类型
//1.type
type a = number[] //给定类型
let a:a = [666]
interface A{
name:string
}
//2. type 与 interface 区别
// interface 可以写成联合类型
interface B extends A {
}
// interface 遇到同名会进行合并
interface A{
age:number
}
type b = string
// type b = number //type同名会报错
//3.type 的联合类型
type c = number[] | string
let c:c = [1]
// 4.type高级用法
// extends 在 type中是包含的意思. extends左侧类型会作为右侧类型的子类型
// 左侧 extends 右侧
type num = 1 extends number? 1 : 0 // num = 1
13.never 类型
// 1. never 永远无法达到的状态
// 2. never 的使用场景
// 3. never 在联合类型中会被忽略
type a = string&number //该类型被推断为 never ,因为变量无法同时成为 number 和 string
// 2.该函数使用就会报错,所以返回值给到 never 更为合适
function err():never {
throw new Error("err")
}
// 死循环同样可以给到 never 返回值,因为它无法返回
function DeathLoop():never {
while(true){
}
}
type Color = "red"| "green"|"blue"//|"yellow" //新增属性 yellow 会直接报错
function color( col:Color) {
switch (col) {
case "red":
break;
case "green":
break;
case "blue":
break;
// 当switch定义完成后,如果type中新增类型没有被case。就会去调用default从而报错
default:
const error:never = col
break
}
}
// 3. never 联合类型中会被忽略
type num = number | string | never //这里num类型为 number | string , never被忽略掉了
14.Symbol 类型 | ES6 yield 生成器 iterator 迭代器
1.Symbol
// 1. symbol 是唯一的,相同值不相等
// 2. symbol.for() for全局symbol是否注册过这个key,如果有直接拿过去用,不会重新创建.没有则创建
// 3. Symbol 解决key重复的问题
// 4. 遍历 Symbol
// 1. symbol
let a1:symbol = Symbol(1)
let a2:symbol = Symbol(1)
console.log(a1 == a2); //打印 false
// 2.symbol.for()使用,()里必须是一个string类型
console.log(Symbol.for("666") === Symbol.for("666")); // true
// 3.Symbol 解决key相同的问题
const obj = {
a:"zhangsan",
// 这里使用了 Symbol 类型,[a1] : [a2] key值相等但是不会覆盖
[a1]:"123",
[a2]:"456"
}
// 使用 Symbol 类型后 ,[a1] : [a2] key值相等但是不会覆盖
console.log(obj); //打印:{ a: 'zhangsan', [Symbol(1)]: '123', [Symbol(1)]: '456' }
// 4. Symbol 的遍历
// for in 遍历返回自身可枚举属性、原型属性,返回值:Key
for (const key in obj) {
console.log(key); //打印: a
}
// Object.keys 遍历返回自身可枚举属性。不包括不可枚举属性,原型属性,Symbol。返回值:数组
console.log(Object.keys(obj)) //打印 ['a']
// Object.getOwnPropertyNames 获取自身所有属性,包括不可枚举属性。不包括Symbol,不包括原型。返回值:数组
console.log(Object.getOwnPropertyNames(obj)) //打印 ['a']
// Object.getOwnPropertySymbols 遍历对象里所有的 Symbol 类型数据。返回值:数组
console.log(Object.getOwnPropertySymbols(obj));// 打印 [ Symbol(1), Symbol(1) ]
//Reflect.ownKeys 完美返回对象里所有属性和 Symbol 属性的方法,不包括原型。返回值:数组
console.log(Reflect.ownKeys(obj)) // 打印 ['a', Symbol(1), Symbol(1) ]
2.yield 生成器 iterator 迭代器 set map 解构赋值 For Of
// 1.生成器
// 2.迭代器
// 3.set map
// 4.iterator
// 5.iterator 语法糖 for of
// 6.解构赋值 底层调用iterator
// 7.forof支持对象,重写对象的Symbol.iterator
// 1.生成器 yield 定义迭代的内容
function* ToMoon() {
yield Promise.resolve("25% in the earth")
yield "50%"
yield "75%"
yield "100% to the Moon"
}
// 通过 next() 进行迭代
// 如果返回值对象里 done 变成 true 。说明该方法无法在进行迭代了
const go = ToMoon()
console.log(go.next()); // { value: Promise { '25% in the earth' }, done: false }
console.log(go.next()); // { value: '50%', done: false }
console.log(go.next()); // { value: '75%', done: false }
console.log(go.next()); // { value: '100% to the Moon', done: false }
console.log(go.next()); // { value: undefined, done: true }
// 2.迭代器 任何伪数组都拥有迭代器 iterator
// 3.Set Map
let set:Set<number> = new Set([1,1,1,2,3,3,5]) //去重
console.log(set); // [1,2,3,5]
let map:Map<string,number> = new Map() //集合
map.set("m1", 123)
function args() {
console.log(arguments); //伪数组
}
// document.querySelectorAll(".div")//伪数组
// 封装迭代方法
const each = (val:any)=>{
// 迭代器使用
let iter:any = val[Symbol.iterator]()
let next:any = {done:false}
// 计数器
let num = 0
while (!next.done) {
next = iter.next()
num++
// done值为false继续遍历
if (!next.done) {
// 迭代器遍历
console.log(`each遍历第:${num} 次`, next.value)
}
}
}
each(set)
/*
输出
each遍历第:1 次 1
each遍历第:2 次 2
each遍历第:3 次 3
each遍历第:4 次 5
*/
// 5.迭代器的语法糖 for of
// for of 不能用于对象类型,对象类型上没有[Symbol.iterator]()方法
// for (const item of {name:"张三"}) {} //报错
for (const item of [{name:"张三"},{name:"李四"}]) {
console.log(item);
// 输出
// { name: '张三' }
// { name: '李四' }
}
// 6.结构赋值,底层原理也是调用iterator
let [a,b] = [1,2];
console.log(a,b);//a = 1, b = 2
[a,b] = [b,a]
console.log(a,b);//a = 2, b = 1
let c = [1,2,3]
let copy = [...c]
console.log(copy); // copy = [1,2,3]
//7.让对象支持for of和数组解构,重写 [Symbol.iterator]()
const obj:any = {
o:1,
o2:2,
// 对象结构赋值或者调用 for of 会触发该方法
[Symbol.iterator]:function(){
// 获取当前对象的 key,得到 obj 的数组
let keys =Reflect.ownKeys(this)
let index = 0
return{
next(){
// index 大于 keys.length 循环结束
if(index>= keys.length){
return {
done:true
}
}else{
return{
done:false,
value:{
key:keys[index],
// 每次获取keys后自增
value:obj[keys[index++]]
}
}
}
}
}
}
}
let newObj = [...obj]
console.log("newObj = ",newObj);
/*
打印
newObj = {
o: 1,
o2: 2,
[Symbol(Symbol.iterator)]: [Function: [Symbol.iterator]]
}
*/
// 使用 for of 迭代
for (const value of obj) {
console.log(value);
}
/*
打印
{ key: 'o', value: 1 }
{ key: 'o2', value: 2 }
{ key: Symbol(Symbol.iterator), value: [Function: [Symbol.iterator]] }
*/
2.泛型 generic
1.泛型使用
// 1.泛型
// 2.interface 泛型
// 3.type 泛型
// 4.函数的泛型
//1.需要类似类型处理不同功能时需要再次创建函数,这时可以使用泛型
function lu(a:number,b:number):number[]{
return [a,b]
}
function lu1(a:number,b:string,c:any):(number|string)[]{
return [a,b]
}
// 泛型 由接收类型设置类型
function fx<T,K>(a:T,b:K):Array<T | K>{
return [a,b]
}
fx("6",1)
fx(false,true )
//2. interface 设置泛型
interface type<T>{
name:T
}
// 使用interface 设置泛型
let data:type<string | number | Array<string>> = {
// name:"123"
// name:123
name:["张三"]
}
// 3. type 设置泛型
type classGeneric<T> = {
// 设置泛型t
name:T
}
const typeGeneric:classGeneric<string>= {name:"张三"}
// 4.函数的泛型
// 可以给泛型默认类型
function sum<T = number,K = number>(a:T,b:K ):Array<T|K> {
return [a,b]
}
sum(true,"999")
// 类型 function sum<boolean, string>(a: boolean, b: string): (string | boolean)[]
// 返回值 [true,"999"]
2.泛型使用场景 ajax封装
const axios = {
get<T>(url:string):Promise<T>{
//这里返回了 Promise<unknown>
return new Promise((res,rej)=>{
let xhr:XMLHttpRequest = new XMLHttpRequest()
xhr.open("GET", url)
xhr.onreadystatechange = ()=>{
if (xhr.readyState == 4 && xhr.status == 200) {
res(JSON.parse(xhr.responseText))
}
}
xhr.send()
})
}
}
interface Data{
name:string,
age:number
}
axios.get<Data>("./16_index.json").then((res)=>{
console.log(res.name,res.age);
})
3.泛型约束 keyof
// 1.泛型约束 extends
// 2.interface约束泛型
// 3.keyof 将 对象类型(类型不是值) 推断成联合类型
// 4.function泛型约束
// 5.keyof遍历
// 1. 在类型后面跟一个 extends 在跟一个约束类型
function add<T extends number>(a:T,b:T) {
return a + b
}
// console.log(add("123","456")) // 使用string类型传参,报错
// 2.interface约束泛型
interface Len{
length:number
}
// 传进来的参数需要有 lengts 属性,length值为number
function arr<T extends Len>(data:T) {
return data.length
}
arr([1,2,3,4,5,6]) //返回值 6
arr('123') //返回值 3
// arr({a:1,b:2}) //报错 object 没有 length 属性
// 3. keyof
let obj = {
name:"张三",
age:22
}
// 对象类型推断成联合类型,方便泛型约束
type Key = keyof typeof obj // Key = "name" | "age"
// 4.function泛型约束,获取value,保证类型安全
// K = “类型” | "类型"
function fn<T extends object,K extends keyof T>(obj:T,key:K) {
return obj[key]
}
// fn(obj,"a") //报错 obj中不存在"a"属性
fn(obj, "name")
// 5.通过泛型约束 extends 和 keyof 将类型变成可选类型
interface Data{
name:string,
age:number,
number:number
}
// 创建一个类型,将上面类型变为可选
// 传进来的类型需要是一个object
type Option<T extends Object> = {
// for in 语句相当于 let key in obj
[Key in keyof T]?:T[Key]
}
type Data2 = Option<Data>
/*
Data2 类型变成了可选
type Data2 = {
name?: string | undefined;
age?: number | undefined;
number?: number | undefined;
}
*/
3.namespace 命名空间
// 1.命名空间 namespace
// 2.多个同名命名空间
// 1.namespace内部相当于函数作用域,底层用的自执行函数。
namespace A{
// 不使用export导出,外部无法读取值
export const a = 1
export var b = 6
export namespace B{
export const a = "123"
// namespace空间的内容会先执行
console.log("我是b");
}
}
console.log(A.a,A.B.a);
// 2.多个同名命名空间使用的是同一个命名空间
namespace A{
// export const a = '666' //报错,无法重复声明const变量
export var b = 1
}
console.log(A.b);
4.三斜杠指令
// 1.三斜杠指令,类似inport
// 1.三斜杠指令
///<reference path="./18_namespace.ts">
/*
文件 ./18_namespace.ts 内容:
namespace A{
export const a = 999
}
*/
namespace A{
export const c = 999
}
console.log(A); //{a:999,c:999}
5.声明文件 .d.ts
// 1. d.ts 文件
// 2. 导入外部插件
// 1.xml在ts中没有声明 d.ts 文件,这里import导入时会报错
// import xml from 'XMLhttpRequest' //报错
// 2. 自定义声明文件
import express from "express" //express.d.ts声明后不再报错
const app = express()
const router = express.Router()
app.use("/",router)
router.get('/api',(req:any,res:any)=>{
res.json({
code:200
})
})
app.listen(8080,()=>{
console.log("服务器启动 http://localhost:8080/api");
})
/******************下面是 ../typings/express.d.ts 文件内容********************/
// express 的 d.ts 声明文件
declare module 'express'{
interface Express{
// const app = express() 会调用该类型
():APP
Router():Router
}
interface APP{
use(path:string,router:any):void
// 监听 回调可选
listen(port:number,cb?:()=>void)
}
interface Router{
get(path:string,router:any):void
}
const express:Express
// 导出express
export default express
}
6.mixin 混入
1.Object.assign
// 1.Object.assign
interface Name{
name:string
}
interface Age{
age:number
}
interface Sex{
sex:string
}
const a:Name = {name:'张三'}
const b:Age = {age:18}
const c:Sex = {sex:"男"}
// 使用Object.assign,类型变成了交叉类型
const obj = Object.assign(a,b,c)//const obj: Name & Age & Sex
2. mixin 重组对象
// 1.mixin
class A{
name:string
getname():string{
return this.name
}
}
class B{
age:number
getAge():number{
return this.age
}
}
class C implements A,B {
name:string = "luxiaohang"
age:number = 18
getname:()=>string
getAge:()=>number
}
mixins(C,[A,B])
function mixins(curCls:any,itemCls:any[]) {
itemCls.forEach(item=>{
// getOwnPropertyNames 遍历对象身上所有可枚举和不可枚举的属性
Object.getOwnPropertyNames(item.prototype).forEach(name=>{
// C对象通过遍历 A,B 对象的原型,拿到原型上的方法挂载到 c 类的原型上
curCls.prototype[name] = item.prototype[name]
})
})
}
// 通过 mixins
let classC = new C()
// c类成功拿到了 B 类和 a 类的方法
console.log(classC.getname()); //打印 lxh
7.装饰器 Decorator
// 1.类装饰器 ClassDecorator
// 2.方法装饰器 MethodDecorator(参数1:原型对象,参数2:函数名称,参数3:PropertyDescriptor)
// 3.参数装饰器 ParameterDecorator 参数修饰器比方法修饰器更早执行
// 4.属性装饰器 PropertyDecorator
// 5.装饰器工厂
// 6.import "reflect-metadata"
// 7.axios
// 8.Reflect.metadata 元数据
/*
元数据的添加 Reflect.metadata
Reflect.metadata(参数1:对象上添加的key,参数2:对象添加的值,参数3:添加元数据的类) //给参数3的类,添加 key:value 元数据
Reflect.metadata(a,"123",obj) // obj{a:"123"}
Reflect.metadata(参数1:对象上添加的key,参数2:对象添加的值,参数3:添加元数据的类,参数4:添加元数据的属性) //给参数3的类的参数4属性,添加 key:value 元数据
Reflect.metadata(a,"123",obj,a) // obj{ a:{ a:"123"}}
元数据获取 Reflect.getMethod
Reflect.getMethod(参数1:对象上的key,参数2:元数据的类) //获取 参数2类 挂载的元数据
Reflect.getMethod(参数1:对象上的key,参数2:元数据的类,参数3:元数据参数2类的属性) //获取 参数2类.参数3 的元数据
*/
import axios from 'axios';
import 'reflect-metadata'
// 1.类装饰器 ClassDecorator
const create:ClassDecorator = (target) =>{
target.prototype.lu = "luxiaohang"
target.prototype.fn = ()=>{
console.log("桀桀桀");
}
}
// 有了装饰器后,可以不破坏原本class结构去添加属性
@create
class People{
}
const man = new People() as any
console.log(man.lu);
// 通过函数科里化,用装饰器给工厂直接传参
const create2 = (name:string)=>{
const fn:ClassDecorator = (target:any)=>{
// 类原型上挂载属性
target.prototype.name = name
target.prototype.fn =()=>{
console.log('base2 装饰器');
}
}
return fn
}
@create2("lxh")
class People2 {
}
// 实例化装饰器对象,需要类型断言因为类型默认为:ClassDecorator
const man2 = new People2() as any
console.log(man2.name);
//===================下面方法在一个类中使用=======================
//类装饰器
const Base = (name:string)=>{
// target 传进来的是 class
const fn:ClassDecorator = (target)=>{
target.prototype.name = "张三干掉" + name
target.prototype.fn = ()=>{
console.log("我是base fn()");
}
}
return fn
}
// 2.方法装饰器
const get = (url:string)=>{
/*
// 装饰器形参
fn:MethodDecorator =(
target:"获得class中所有方法", // { getList: [Function (anonymous)], create: [Function (anonymous)] }
propertyKey:"获得当前方法名", // getList
descriptor:"获得一个对象 .value给方法回传的内容"{value: [Function (anonymous)],writable: true,enumerable: true,configurable: true}
)
*/
//descriptor需要给定PropertyDescriptor 属性,不然参数不可用
const fn:MethodDecorator =(target,propertyKey,descriptor:PropertyDescriptor)=>{
// 获取到字符串 list
const key = Reflect.getMetadata('getList', target)
axios.get(url)
.then(res => {
// 通过 descriptor 回传成功的回调.返回元数据 value
descriptor.value(res.data[key])
})
}
return fn
}
// 3.参数装饰器
const List = ()=>{
// 参数修饰器需要加类型 ParameterDecorator 。参数修饰器
const fn:ParameterDecorator = (target,key,index)=>{
//设置一个 getList 值为 list 供元数据获取
Reflect.defineMetadata("getList","list",target)
}
return fn
}
// 4.属性修饰器
const Name:PropertyDecorator = (target,propertyKey)=>{
console.log(target, propertyKey); //{},name
}
@Base('LXH')
class Http{
// 4.属性修饰器
@Name
name:string
constructor(){
this.name = "张三"
}
// 2.指定一个方法修饰器
@get('http://localhost:8080')
// 3.指定一个参数修饰器
getList(@List() data:any){
console.log("我收到了后端请求",data);
}
create(){
}
}
8.localstorage本地存储
1.结构目录
//结构目录
|--src
|--enum
|--index.ts
|--type
|--index.ts
|--index.ts
2.enum文件夹
//字典 expire 过期时间 key,permanent永久不过期
export enum Dictionaries{
permanent = "permanent",
//签名
expire = "__expire__"
}
3.type文件夹
import {Dictionaries} from '../enum';
export type Key = string
// expire localStorage过期时间类型
export type Expire = Dictionaries.permanent | number //永久 或者 时间戳
// Data 类型
export interface Data<T>{
value:T,
[Dictionaries.expire]:Expire
}
// Value 类型
export interface Result<T>{
message:string
value:T|null
}
export interface StorageCls{
get:<T>(key:Key)=>void
set:<T>(key:Key,value:T,expire:Expire)=>void
remove:(key:Key)=>void
clear:()=>void
}
4.index.ts
import {StorageCls,Key,Expire,Data,Result} from './type'
import {Dictionaries} from "./enum"
export class Storage implements StorageCls{
// 默认存储时间为永久
set<T>(key:Key,value:T,expire:Expire = Dictionaries.permanent){
const data:Data<T> = {
value,//用户value
[Dictionaries.expire]:expire //过期时间
}
// 存放用户数据
localStorage.setItem(key, JSON.stringify(data))
}
// 取出数据,返回值 T 或者 null
get<T>(key:Key):Result<T | null>{
const result = localStorage.getItem(key)
if (result) {
// 取到数据存放为JSON
const data:Data<T> = JSON.parse(result)
const now = new Date().getTime()
// 如果过期时间小于当前时间:返回已过期
if(typeof data[Dictionaries.expire] == "number" && data[Dictionaries.expire]<now){
// 清除key
this.remove(key)
return {
message:`您的 ${key}已过期`,
value:null
}
}else{
return{
message:"成功",
value:data.value
}
}
}else{
return {
message:"无效值",
value:null
}
}
}
// 移除指定的 localStorage
remove(key:Key){
localStorage.removeItem(key)
}
// 清空 localStorage
clear(){
localStorage.clear()
}
}
const save = new Storage();
save.set("a", 123, new Date().getTime() + 10000);
setInterval(() => {
console.log(save.get("a"));
}, 500);
9.Dispatch发布订阅
// 订阅发布模式
interface Events{
on:(name:string,fn:Function)=>void
emit:(name:string,...args:Array<any>)=>void
off:(name:string)=>void
once:(name:string,fn:Function)=>void
}
interface List{
[key:string]:Array<Function>
}
class Dispatch implements Events {
list:List
constructor(){
this.list = {}
}
//1.订阅
//往list对象传入回调,emit发布时执行所有回调方法
on(name:string,cb:Function){
const callback = this.list[name] || []
callback.push(cb)
this.list[name] = callback
}
// 2.发布
// ...args向on传的参数
emit(name:string,...args:Array<any>){
// 获取当前class的list内容
let eventName= this.list[name]
// 判断 list.name 是否存在
if (eventName) {
// 存在
eventName.forEach(item=> {
item.apply(this,args)
});
}else{
console.error(`名称输入有误${name}`)
}
}
off(name:string){
const list = this.list[name]
console.log(list);
if (list.length) {
this.list[name] = []
console.log("删除成功");
} else{
console.error(`删除失败,该对象不存在`);
}
}
// 向list中传入一个one的回调函数,执行后删除该函数
once(name:string,fn:Function){
let one = (...args:Array<any>)=>{
fn.apply(this,args)
this.off(name)
}
this.on(name, one)
}
}
const list = new Dispatch()
list.on("name",(...args:Array<any>)=>{
console.log("添加了name 1",args);
})
list.on("name",(...args:Array<any>)=>{
console.log("添加了name 2",args);
})
list.emit("name","我是通过emit添加的内容",{name:"luxiaohang"})
list.off("name")
list.off("name")
10.proxy代理
1.proxy
// 1.proxy
// 2.proxy 拦截
// proxy 代理 13 个方法
// Reflect 代理 13 个方法
let person = {name:"陆小杭",age:24}
person.name //取值
person.name = 'xxx' //赋值
let personProxy = new Proxy(person,{
// 取值时调用
get(target, p, receiver) {
},
// 赋值时调用
// person name
set(target,key,value,receiver){
return true
},
// 拦截函数的调用
apply(){
},
// 拦截 in 操作符
has(){
return true
},
// 拦截forin
ownKeys(){
return []
},
// 拦截 new 操作符
construct(){
return {}
}
// 拦截删除操作
/* deleteProperty(target,p){
} */
})
// 2.proxy 拦截
let person = {name:"lxh",age:24}
let personProxy = new Proxy(person,{
get(target,key,receiver){
if (target.age >18) {
return Reflect.get(target,key,receiver)
}else{
return "无法获取到小于 18 岁的内容 "
}
}
})
2.观察者模式
// 观察者模式
const list:Set<Function> = new Set()
const autorun = (cb:Function)=>{
if (!list.has(cb)) {
// 没有就添加
list.add(cb)
}
}
const observable = <T extends object>(params:T) =>{
return new Proxy(params,{
set(target, p, value, receiver) {
const result = Reflect.set(target, p, value, receiver)
// 这里调用了autorun()这个方法
list.forEach(fn => fn())
return result
},
})
}
const ObserverProxy = observable({name:'qq',attr:"一只企鹅"})
autorun(()=>{
console.log("有变化了");
})
ObserverProxy.attr = "一匹马"
ObserverProxy.attr = "666"
11.协变与逆变
// 1.协变
// 2.逆变
// 鸭子类型 十分相似
interface A{
name:string
age:number
}
interface B{
name:string
age:number
sex:string
}
let a:A = {
name:"老墨吃鱼",
age:33
}
let b:B = {
name:"高启强",
age:23,
sex:"男"
}
// 1.协变 对象多可以传给少的
a = b
// 2.逆变 逆变是安全的
let fna =(params:A)=>{
}
let fnb =(params:B)=>{
}
fnb = fna
12.weakmap | weakset
// 1.weakmap 是一个弱引用,不被计入垃圾回收机制。
// weakmap指向的对象没有目标引用时,weakmap也将取消指向
// 2.weakset 也是一个弱引用,不会计入垃圾回收机制。
let obj:any = { //引用次数 1
name:"张三"
}
let obj2:any = obj //引用次数 2
// weakmap key必须是对象类型
const weakM:WeakMap<object,any> = new WeakMap() //引用次数 2,不计入引用次数
weakM.set(obj, {okk:"123456"})
console.log(weakM.get(obj)) //打印 {okk:"123456"}
obj = null
obj2 = {}
console.log(weakM.get(obj))//没有引用了 打印 undefine
// weakset 内容也需要是object类型
new WeakSet([])
13.Partial & Pick
type Person = {
name:string
age:number
text:string
}
// Pick 将类型拆分出来
type p = Pick<Person,'age'|'name'>
// 使用 keyof 语法
function fn<T extends Person,K extends keyof T>(person:T,p:K){
console.log(person[p]);
}
fn({name:"张三",age:18,text:"111"},"name")
type key = "name" | "age" | "sex"
type Par<T> = {
[P in keyof T]?:T[P];
}
type keyof = Par<Person>
14. readonly & record
// 1.readonly
// 2.record
type People = "name"|"age"|"sex"
type partial<T> = {
readonly [P in keyof T]:T[P]
}
const obj = {
name:"zhangsan",
age:18,
sex:"nv"
}
function fn<T>(params:partial<T>) {
// console.log(params);
return params
}
// 通过 readonly 修饰后函数只能读取不能修改
const cb = fn(obj)
// cb.name = 1 //报错.name 只读
// 2.record
type Rec<K extends keyof any,T>={
[P in K] :T
}
type person ={
name:string,
age:number,
sex:string
}
type Str = "A"|"B"|"C"
type newperson = Rec<Str,person>
/*
newperson的类型
type newperson = {
A: person;
B: person;
C: person;
}
*/
15.infer 推断
//1. T[number] T[any] 数组元数据 返回数组元素的类型
//2. <infer T> 返回数组元素的类型
//3. infer 数组元素的类型
//4. infer 获取数组中的元素
// 定义一个类型,如果是数组类型 就返回数组元素的类型
// 否则就传入什么类型 返回什么类型
type TYPE<T> = T extends Array<any> ? T[number] : T
type A = TYPE<(string | number)[]>
// 这里返回值为啥是 string | Number 呀
// A 不应该是 (string | number)[] 吗?
// type A = (string | Number)[]
type B = TYPE<boolean>
type t<T> = number[]
// 2.infer:可以返回一个数组元素的类型.这里的 U 不是泛型只是一个占位符
// <infer U> u是数组元素的类型
type TYPE2<T> = T extends Array<infer U>? U : T
type obj2 =TYPE2<(string|number|Function)[]>; //obj2 类型:string|number|Function
type obj2type = TYPE2<obj2>
// 3.infer 数组元素的类型
type TYPE3<T> = T extends Array<infer U>? U : never
// 元组
type arr = [number,string]
type uni = TYPE3<arr> // uni 类型 : string | number
type err = TYPE3<boolean>// err 类型 : never
// 4.infer 获取数组中的元素
type arr2 = [[],'a',"b","c"]
// 判断数组是否存在 3 个参数,没有返回 []
type TYPE4<T extends any[]> = T extends [infer one,infer tow,infer three] ? tow : []
type getType4 = TYPE4<arr2>
// 获取数组中最第一个数据类型
type TYPE5<T extends any[]> = T extends [infer one,...any[]] ? one: any
type getType5 = TYPE5<arr2> //类型 []
// 获取数组中最后一个的数据类型
type TYPE6<T extends any[]> = T extends [...any[],infer Last] ? Last: any
type getType6 = TYPE6<arr2> //类型 c
// 获取数组中多个数据类型
type TYPE7<T extends any[]> = T extends [...infer first,infer Last] ? first: []
type getType7 = TYPE7<arr2> //类型 [[],'a','b']
// 放弃数组中的第一个数据类型
type TYPE8<T extends any[]> = T extends [unknown,...infer Last]?Last:[]
type getType8 = TYPE8<arr2> //类型 ['a','b',"c"]
// infer 递归
type Arr = [1,2,3,4,5,6]
type ReversArr<T> = T extends [infer first,...infer last] ? [...ReversArr<last>,first]:T
type recursion = ReversArr<Arr>
TS 在 VUE 中的使用
1.ts+ref
//定义简单数据类型
//需要注意,指定了一个泛型参数但没有给出初始值,那么最后得到的就将是一个包含 undefined 的联合类型:
// 推导得到的类型:Ref<boolean | undefined>
const show = ref<boolean>(false);
或者
const show:Ref<string> = ref(false);
//简单数据类型赋值
const handleShow = ():void=>{
show.value = true
}
//---------------------------------------------------------------------------------------
//定义复杂数据类型
interface ArrListType {
name:string
age:number
des:string
}
type ObjListType = partial<ArrListType>
const arrList = ref<ArrListType[]>([])
const objData = ref<ObjListType>({})
//复杂数据类型赋值
const handleArr = ()=>{
//mock数据
const res = [{name:'x',age:10,des:'x'},{name:'y',age:11,des:'y'}]
arrList.value = res
}
const handleObj = ()=>{
//mock数据
const res = {name:'x',age:10,des:'x'}
arrList.value = res
}
2.获取组件 ref 实例+ts
使用 vue3 和 ts 时,为了获取 组件 ref 实例,就需要在 ref 函数的泛型中指定类型。如何获取组件的类型呢?vue 官方文档中 TypeScript 支持里已经告诉我们了一个获取组件类型的方法,InstanceType<typeof 组件名称>,使用方式如下:
//为了获取组件的类型,我们首先需要通过 typeof 得到其类型,再使用 TypeScript 内置的 InstanceType 工具类型来获取其实例类型:
const $userForm = ref<InstanceType<typeof userForm>|null>(null);
3.ref实例+ts
//子组件
<NewsDialog ref="news" @refreshData="getList()"></NewsDialog>
<script>
//导入子组件
import NewsDialog from './components/NewsDialog.vue'
//获取子组件实例
const news = ref<InstanceType<typeof NewsDialog>>()
//打开消息弹窗
const openNewsDialog = (): void => {
news.value?.showDialog()
}
//子组件暴露方法
defineExpose({
showDialog,
});
</script>
处理原生 DOM 事件时 + ts //input标签
const input = ref<HTMLElement | null>(null); //获取焦点 (input.value as HTMLInputElement).focus(); ts结合prop使用(父传子) 注意:传递给 defineProps 的泛型参数本身不能是一个导入的类型:
//vue3+js的写法 const props = defineProps({ id:{ type:Number, default:0 } arr{ type:Array default:()=>[] } }) //vue3+ts写法 interface PropType = { id:number arr:string[] } const props = defineProps() //props 声明默认值 通过 withDefaults 编译器宏解决 推荐 const props = withDefaults(defineProps(), { id: 1, arr: () => ['one', 'two'] }) //下面的默认值写法暂时还处在实验性阶段,了解为主 const { id, arr=[] } = defineProps() ts结合emit使用(子传父) //vue3+js的写法 const emits = defineEmits(['change', 'update']) //vue3+ts写法 const emits = defineEmits<{ (e:'change'):void, (e:'update',value:string):void }>() ts+computed computed() 会自动从其计算函数的返回值上推导出类型:
const count = ref(0) // 推导得到的类型:ComputedRef 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 类型则会报错 }) provide / inject + ts import { provide, inject } from 'vue' import type { InjectionKey } from 'vue' //建议将key抽出去,方便复用 const key = Symbol() as InjectionKey
provide(key, 'foo') // 若提供的是非字符串值会导致错误
const foo = inject(key) // foo 的类型:string | undefined ts+pinia 在pinia里已经去掉了mutation
首先要安装全局依赖npm i pinia
//封装使用的pinia 在store/index.ts里 //导入defineStore import { defineStore } from 'pinia' //导入接口 import { getBoxConfigApi } from '@/api/GuardBox' //定义数据接口 export interface giftInfoType { use_gift_id: number count: number level: number gift: { id: number name: string } } //定义数据接口 interface AllConfigType { all_config: Partial<{ title_img: string date_range: string[] year_select: string[] ttl: string constellation: string user_level: string }> gift_info: giftInfoType[] } //使用pinia export const useStore = defineStore('main', { state: (): AllConfigType => ({ all_config: {}, gift_info: [], }), actions: { async getConfig(level = 1) { const { data } = await getBoxConfigApi({ level }) this.all_config = data.all_config this.gift_info = data.gift_info return data }, }, })
//引用pinia 在homePage.vue里
<script setup lang="ts">
import { useStore } from '@/store'
const store = useStore()
// 礼物配置信息
const giftConfigInfo = ref<giftInfoType[]>([])
// 获取礼物信息
const getGiftInfo = async (): Promise<void> => {
const { gift_info } = await store.getConfig(activeIndex.value)
giftConfigInfo.value = gift_info
}
getGiftInfo()
</script>
3.ts定义异步空返回值函数
const getList = async (): Promise<void> => {
const {data} = await getListApi();
...
}