(二十一)declare
声明文件 xxx.d.ts
在使用第三方时,必须有声明文件,不然会报错。
一些火的库,社区会编写声明文件, npm i -S @types/xxxx 。(比如echarts)
比较冷门的库,没有人编写声明文件,就需要手写声明文件。(比如旧版的express,现在有了)
如果没有,那就需要创建一个xx.d.ts文件,这里放在了typings下。
//express.d.ts
declare module 'express'{ // 手写一个声明文件,express这个是很简单的写法
interface Router{
get(path:string,cb:(req:any,res:any)=>void):void
}
interface App {
use:(path:string,router:any)=>void
listen(port:number,cb?:()=>void)
}
interface Express{
():App
Router():Router
}
const express : Express
export default express
}
还可以扩充全局变量
declare var qun:number
// 全局函数
declare function xxx(params:type) {
}
// 全局类,枚举,命名空间,global 等等
declare class Cust {
constructor(parameters) {
}
}
(二十二) mixins
mixins 混入,对象混入,类的混入。合并后其中都包含的属性。
interface Name{
name:string
}
interface Age{
age:number
}
interface Sex{
sex:number
}
let a:Name = {name:'xx'}
let b:Age = {age:33}
let c:Sex = {sex:1}
let obj = Object.assign(a,b,c) //Name & Age & Sex 混入
class A{
type:boolean
changeType(){
this.type = !this.type
}
}
class B{
name:string
getName():string{
return this.name
}
}
// 约束类型 占位符(和继承有区别)
class C implements A,B{
type:boolean = false
name:string = '阿飞'
changeType:()=>void
getName:()=>string
}
// 手动挂载到原型上
function mixins(curCls:any,itemCls:any[]){
itemCls.forEach(item=>{
Object.getOwnPropertyNames(item.prototype).forEach(name=>{
curCls.prototype[name] = item.prototype[name]
})
})
}
mixins(C,[A,B])
let ccc = new C()
console.log(ccc.type);
ccc.changeType()
(二十三)装饰器 Decorator
需要配置tsconfig文件:"experimentalDecorators": true, "emitDecoratorMetadata": true,
就是简化一些操作,对使用装饰器的都执行对应的操作。在不了解其中方法和属性时,不破坏结构,装饰器可以实现。
1.类装饰器 ClassDecorator
// 1.类装饰器 ClassDecorator
// 定义一个方法,指定类型,@+名称 使用 就可以在编译过程中调用
// 执行会回传,target(是类的构造函数,不是原型对象)
const Base:ClassDecorator = (target) => {
// console.log(target);
target.prototype.__name = '小'
target.prototype.fn = ()=>{
console.log('憨憨');
}
}
// 好处1:可以在不了解类中方法和属性时,不想破坏其中结构时,装饰器可以添加
// !!! 如果这种方法不支持的话,profill的话, Base(Http) 的调用也是可以的
@Base
class Http{
}
const http = new Http() as any
http.fn()
2.属性装饰器 PropertyDecorator
const Name:PropertyDecorator = (target,key)=>{
// target原型对象,key 属性名称
console.log(target,key);
}
// 2.属性装饰器 PropertyDecorator
@Name
elname:string
3.参数装饰器 ParameterDecorator
// 通过 reflect-metadata reflect原数据来存储 ,安装一下 npm i reflect-metadata
const Result = ()=>{
const fn:ParameterDecorator = (target,key,index)=>{
// target原型对象,key 函数名称,index 参数位置
// console.log(target,key,index);
Reflect.defineMetadata('_key','data',target) // 存储 命名,值,对象
}
return fn
}
getList( @Result() data:any){
console.log(data);
}
4.方法装饰器 MethodDecorator PropertyDescriptor
// 4.方法装饰器 MethodDecorator PropertyDescriptor
// 可以简化请求中的方法,直接输入url,可以返回data
const Get = (url:string)=>{
const fn:MethodDecorator = (target,key,descriptor:PropertyDescriptor)=>{
// target原型对象,key方法名称,descriptor描述符
// console.log(target,key,descriptor);
const _key = Reflect.getMetadata('_key',target) // 读取 命名,对象
axios.get(url).then(res=>{
descriptor.value(_key ? res.data[_key] : res.data) // 返回到方法里
})
}
return fn
}
@Get('https://mon.zijieapi.com/monitor_web/settings/browser-settings?bid=2608&store=1')
getList( @Result() data:any){
console.log(data);
}
5.装饰器工厂(函数柯力化)
// 装饰器工厂,函数柯力化
const Base2 = (name:string) => {
const fn:ClassDecorator = (target)=>{
target.prototype.__name = name
target.prototype.fn = ()=>{
console.log('憨憨');
}
}
return fn
}
6.import 'reflect-metadata',,reflect原数据来存储 ,安装一下 npm i reflect-metadata。
包含以上装饰器的类:
@Base2('yu')
class Http2{
// 2.属性装饰器 PropertyDecorator
@Name
elname:string
constructor(){
this.elname = 'weiw'
}
// 3.参数装饰器 ParameterDecorator
@Get('https://mon.zijieapi.com/monitor_web/settings/browser-settings?bid=2608&store=1')
getList( @Result() data:any){
console.log(data);
}
create(){
}
}
const http2 = new Http2() as any
(二十四)proxy
proxy代理 13个方法,参数一模一样,拦截器。
reflect 反射 13个方法,参数一模一样。
proxy 支持对象,数组,函数,set,map,引用类型
let personProxy = new Proxy(person,{....})
let personProxy = new Proxy(person,{
// 取值
get(){
},
// 赋值
// target 目标自身,key key值,value 赋值,receiver 为了防止箭头函数的错乱,相当于自身
set(target,key,value,receiver){
return true
},
// 拦截函数调用
apply(){
},
// 拦截 in 操作符
has(){
},
// 拦截 for ..in.. 操作
ownKeys(){
},
// 拦截 new 操作符
construct(){
},
// 拦截 delete 操作
deleteProperty(){
},
// 拦截 Object.defineProperty
defineProperty(){},
// 拦截 Object.getOwnPropertyDescriptor
getOwnPropertyDescriptor(){},
// 拦截 Object.preventExtensions
preventExtensions(){},
// 拦截 Object.getPrototypeOf(proxy)
getPrototypeOf(){},
// 拦截 Object.isExtensible
isExtensible(){},
// 拦截 Object.setPrototypeOf
setPrototypeOf(){},
})
reflect 可以直接操作对象,方法参数一模一样,方便我们的操作。
let personProxy = new Proxy(person,{
get(target,key,receiver){
if(target.age <= 19){
return Reflect.get(target,key,receiver) // Reflect 可以直接操作对象,方法一模一样
}else{
return 'okkk'
}
}
})
// console.log(personProxy.age);
实现 mobx 的 observable 观察者模式
// 存储器
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,key,value,receiver){
const result = Reflect.set(target,key,value,receiver) // 返回布尔值
list.forEach(fn => fn())
return result
}
})
}
const ppProxy = observable({name:'hihi',attr:'发将诶啊哦'})
autorun(()=>{
console.log('有变化了');
})
ppProxy.attr = '基金哦溶解'
ppProxy.name = '接口分'
(二十五)协变
鸭子类型:如果走起路来像鸭子,叫起来也像鸭子,那么它就是鸭子。
一个对象的特征不是由父级决定的,而是通过对象的方法决定的。
协变----子类型如果包含主类型的所有属性,那么可以被赋值。
逆变----通常发生在函数上。(一定是安全的。)
interface A {
name:string
age:number
}
interface B {
name:string
age:number
sex:string
}
let a:A = {
name:'吃鱼了',
age:34
}
let b:B = {
name:'高强度',
age:24,
sex:'男'
}
// 协变 -- 子类型如果包含主类型的所有属性,那么可以被赋值
a = b
// 逆变 -- 通常发生在函数上
let fna = (p:A)=>{}
let fnb = (p:B)=>{}
// 一定是安全的
fnb = fna
// 如果要实现 双向协变 需要tsconfig中开启 "strictFunctionTypes": false,
fna = fnb
(二十六)weakmap
(如果在 package.json 中加 type:module属性,可以使用 ts-node-esm )
weakmap,weakset 弱引用
和垃圾回收机制有关,不会被计入垃圾回收。weakmap的key只能是引用类型。引用类型释放后,weakmap也会消失。
(有时控制台可以输出,但是取不到值,v8的回收需要时间。200ms,因为不稳定,所以不允许取键值)
// weakmap weakset 弱引用
// 和垃圾回收机制有关, 不会被计入垃圾回收。
// weakmap和map的区别是,weakmap的key只能是引用类型
let obj:any = {name:'呜呜呜'} // 引用1
let aabb:any = obj // 引用2
let weakmap:WeakMap<object,any> = new WeakMap()
weakmap.set(obj,'asdf') // 引用还是2 ,不会计入垃圾回收
// 所以 obj 和 aabb 释放之后,weakmap也会消失。
// 有时控制台可以输出,但是取不到值,v8的回收需要时间。200ms,因为不稳定,所以不允许取键值
// 释放
// obj = null // -1
// aabb = null // -1
console.log(weakmap.get(obj));
(二十七) Partial
Partial 可选的
Pick 摘出,筛选
Readonly 只读
Record 同时约束 key,value
type Person = {
name:string,
age:number,
text:string
}
// type Par<T> = {
// [P in keyof T]?:T[P]
// }
type P1 = Partial<Person>
// type PPP<T,K extends keyof T> = {
// [P in K]:T[P]
// }
type P2 = Pick<Person,'age'|'name'>
// type R<T> = {
// readonly [P in keyof T]:T[P]
// }
type P3 = Readonly<Person>
// keyof any 相当于 string | number | symbol
// type Rec<K extends keyof any,T> = {
// [P in K] : T
// }
type P4 = Record<string,Person>
(二十八)infer
infer 占位符
定义一个类型:如果是数组类型就返回数组类型,否则,传入什么类型,就返回什么类型。
// type TY<T> = T extends Array<any> ? T[number] : T
// 这里infer是一个占位符,读Array的类型,取别名U使用
type TY<T> = T extends Array<infer U> ? U : T
type AA = TY<string[]> // string
type BB = TY<boolean> // boolean
还可以进行更加细致的筛选和占位
type Arr = ["a", "b", "c"]
// type First<T extends any[]> = T extends [infer one,infer two,infer three] ? one :[]
type First<T extends any[]> = T extends [infer one, ...any[]] ? one : [] // 取头部元素
type Last<T extends any[]> = T extends [...any[], infer last] ? last : [] // 取尾部元素
type Pop<T extends any[]> = T extends [...infer Rest, unknown] ? Rest : [] // 取除最后一个的其他元素
type Shif<T extends any[]> = T extends [unknown, ...infer Rest] ? Rest : [] // 取除第一个的其他元素
type aa = First<Arr>
type ll = Last<Arr>
type pp = Pop<Arr>
还可以递归,实现反向数组
// infer 递归
// 实现 反向
type arr = [1, 2, 3, 4]
type ReverArr<T extends any[]> = T extends [infer first, ...infer rest]
? [...ReverArr<rest>, first]
: T
type arrb = ReverArr<arr> // [4,3,2,1]