#前端重铸之路 Day4 🔥🔥🔥🔥

69 阅读8分钟

前置涩话

掘金的小伙伴们大家好,这里是家里蹲选手sin~ 今天是重铸前端的第四天,今天就学习一下当下热门的TypeScript吧!首先来点ts基础,再收集一些知识点并记录一下吧。

✅ ts基础知识

为什么用TS?

1.ts是js的加强版,给js增加了可选的静态类型和面向对象编程。
2.ts是面向对象语言,包含类和接口的概念。
3.ts开发时就能给出编译错误,js运行时才能发现。
4.ts作为强类型语言,明确数据类型,代码可读性比较强。
5.ts可选链属性。

基础类型

let str:string = "大林忽悠"
let num:number = NaN
let bool:boolean = true
let sym:symbol = Symbol()
let n:null = null
let u:undefined = undefined

console.log(str)

any 任意类型 unknown 不知道的类型
类型等级:
1.any unknown top type 顶级类型
2.Object
3.Number String Boolean... 自身构造函数
4.number string boolean... 实例对象
5.1 '大林忽悠' false...
6.never
unknown只能赋值给自身或者any
unknown没有办法读任何属性,方法也不可以调用
unknown比any安全

//Object 和 object
//Object可以任意赋值
//object常用于类型约束,除了原始类型外的引用类型

let a:{} = {num: 1} //new Object 无法对变量进行任何赋值操作

数组

数组普通类型
let arr:number[] = [1,2,3]
let arr:boolean[] = [true,false]
let arr:Array = [true,false]
对象数组
interface X {
name:string
age?:number
}

let arr:X[] = [{name: "大林忽悠"}]

二维数组
let array:number[][] = [[1],[2],[3]]
let array:Array<Array> = [[1],[2],[3]]
杂烩数组
let array:any[] = [1,'asd',true,{}]

function fn2(...args:any[]) {
let a:IArguments = arguments //定义类数组
console.log(args)
}

fn2(1,2,3)

Symbol

for会全局寻找有没有注册过这个key,有直接用,没有就创建
Symbol.for('lin') === Symbol.for('lin') true

迭代器 [Symbol.iterator]
迭代器语法糖 for of
解构 数组解构底层原理是迭代器 对象不是

函数

1.函数定义类型和返回值 | 箭头函数定义类型和返回值
2.函数默认参数 | 函数可选参数
3.参数是一个对象如何定义
4.函数this类型
5.函数重载

// function add(a:number = 10,b?:number):number { 默认值和可选
// return a + b
// }
const add = (a:number,b:number):number => a+b

interface User {
name:string
age?:number
}

function P(user:User):User {
return user
}

console.log(P({name: '大林忽悠',age: 25}))

interface Obj {
user:number[]
add:(this:Obj,num:number)=>void
}
//ts可以定义this类型 必须是第一个参数定义this的类型
let obj:Obj = {
user:[1,2,3],
add(this:Obj,num:number) {
this.user.push(num)
}
}

obj.add(4)
console.log(obj)

函数重载,根据参数不同重载不同函数返回不同结果

let user:number[] = [1,2,3]  
function findNum(add:number[]):number[]    
function findNum(id:number[]):number[]  
function findNum():number[]  
function findNum(ids?:number[] | number[]):number[] {  
    if(typeof ids == 'number') {  
        return user.filter(v=>v == ids)  
    }else if (Array.isArray(ids)){  
        user.push(...ids)  
        return user  
    }else {  
        return user  
    }  
} 

元组:规定了数量,对应位置对应类型

let arr2:[number,boolean] = [1,false]

枚举类型 (数字枚举 字符串枚举 异构枚举 接口枚举 const枚举 反向映射)

    enum Color {  //red = 0, green = 1, blue = 2  
        red = 1,  //red = 1, green = 2, blue = 3  增长枚举  
        green,  
        blue  
    }  

    enum Types {  
        success  
    }  

    let success:number = Types.success  
    let key = Types[success]  

    console.log(`value---${success}`,`key---${key}`)  

类型断言

a as string  <string>a  

内置对象

//let num:Number = new Number(1)
let date:Date = new Date()
let reg:RegExp = new RegExp(/\w/)

//let div = document.querySelector('footer') as Element
//let div:NodeList = document.querySelectorAll('footer')
let div:NodeListOf<HTMLDivElement | HTMLElement> = document.querySelectorAll('div')

let local:Storage = localStorage
let lo:Location = location
let promise:Promise = new Promise((r)=>r(1))
let cookie:string = document.cookie

联合类型

联合类型使用 | 操作符表示允许一个值可以是多种类型中的一种。它表示“或”的关系,即一个变量可以是类型A或类型B

    let value:number | string
    value = 26
    value = '大林'

交叉类型

交叉类型使用 & 符号将多个类型合并成一个新类型,这个新类型同时拥有所有输入类型的属性和方法。它表示“与”的关系,即一个对象同时满足多个类型的条件。

    interface People{  
    name:string  
    age:number  
}  

interface Man{  
    sex:number  
}  
 
const dalin = (man:People & Man):void => {  
    console.log(man)  
}  
dalin({  
    name: '大林忽悠',  
    age: 25,  
    sex: 1  
})  

class

1.class的基本用法 继承(extends) 和 类型约束(implements)
2.class的修饰符 readonly(不可修改,只读) private(私有属性) protected(可继承私有) public
3.super原理
4.静态方法(static),只能通过类去调用
5.get set

虚拟dom 简单版

    interface Options {  
    el:string | HTMLElement  
}  

interface VueCls {  
    options:Options  
    init():void  
}  

interface Vnode {  
    tag:string  
    text?:string  
    children?:Vnode[]  
}  
    class Dom {  
    //创建节点的方法  
    private createElement(el:string) {  
        return document.createElement(el)  
    }
    //填充文本的方法
    private setText (el:HTMLElement, text:string | null) {
        el.textContent = text
    }
    //渲染函数
    protected render(data:Vnode) {
        let root = this.createElement(data.tag)
        if(data.children && Array.isArray(data.children)) {
            data.children.forEach(item => {
                let child = this.render(item)
                root.appendChild(child)
            })
        } else {
            data.text && this.setText(root, data.text)
        }
        return root
    }
}

class Vue extends Dom implements VueCls{
    options: Options
    constructor (options: Options) {
        super()    //父类的prototype.constructor.call
        this.options = options
        this.init()
    }
    public init(): void {
        let data:Vnode = {
            tag: "div",
            children: [
                {
                   tag: "section",
                   text: "我是子节点1"
                },
                {
                    tag: "section",
                    text: "我是子节点2"
                 },
            ]
        }
        let app = typeof this.options.el == 'string' ? document.querySelector(this.options.el) : this.options.el
        app && app.appendChild(this.render(data))
    }
}

new Vue({
    el: "#app"
})

基类 抽象类

abstract所定义的类
abstract所定义的方法都只能描述不能进行实现
抽象类无法实例化但可以被派生类继承

    abstract class King {  
    name:string  
    constructor (name?:string) {  
        this.name = name  
    }  
    getName ():string{  
        return this.name  
    }  
    abstract init(name:string):void  
}  
   
class Lin extends King{  
    constructor () {  
        super()  
    }  
    init (name:string) {  

    }  
    setName (name:string) {  
        this.name = name  
    }  
}  

const lin = new Lin()  
lin.setName('大林忽悠')  

console.log(lin.getName())  

interface接口

interface 重名的会进行重合
interface 任意key [propName:strng]:any (索引签名)
interface ?操作符(可选值) readonly(让值不可更改)
interface 接口继承 extends
interface 定义函数类型
不能多属性,也不能少属性

interface Axxsxs{  
    name:string  
    age?:number  
    readonly cb:()=>boolean  
}  
let x:Axxsxs = {  
    name: '大林忽悠',  
    age: 25,  
    cb:()=>{  
        return false  
    }  
}  

interface Fn {   
    (name:string):number[]  
}  
const fn:Fn = function (name:string) {  
    return [1]  
}  

type类型别名

与interface的区别:

1.不能用extends去继承(只能用交叉类型 & )
2.可以用联合类型 | ,interface不可以
3.interface重名合并,type不会
type s = number[] | string
let str1:s = '大林忽悠'

extends 在 types 是包含的意思(左边的值会作为右边类型的子类型)
type num = 1 extends number ? 1 : 0

implements 和 extends 的用法与区别

extends 继承

用途

  • 类继承类:子类继承父类的属性和方法(单继承)。
  • 接口继承接口:合并多个接口的定义(多继承)。
  • 类继承抽象类:继承抽象类的部分实现并强制重写抽象方法。

特点

  • 子类获得父类的实现代码(方法体、属性值)。
  • 支持方法重写(override)和通过 super 调用父类方法。
  • 接口继承仅合并类型定义,无运行时代码 。

implements 实现

用途

  • 类实现接口:强制类实现接口中定义的所有属性和方法。
  • 类可实现多个接口(多继承契约)。
    
interface Alarm { alert(): void }
interface Light { switch(): void }
 
// 类实现多个接口
class Car implements Alarm, Light {
  alert() { /* 实现 */ }
  switch() { /* 实现 */ }

特点

  • 仅类型约束,不继承任何实现代码。
  • 接口可定义方法签名和属性类型,但不提供具体逻辑。
  • 若未实现接口成员,会触发编译错误 。

泛型 <T,K>

泛型约束
keyof 把对象的key推断成联合类型 如下面obj1变成 name | sex
function lin1(a:T,b:T):Array{
return [a,b] }

let obj1 = { name: '大林忽悠', sex: '男' }

type Key = keyof typeof obj1

function ob<T extends object,K extends keyof T>(obj:T,key:K){ return obj[key] }

ob(obj1, 'name')

高级用法 Partial

interface Data {
name:string
age:number
sex:string
}
//for (let key in obj)
type Options1 = {
readonly [key in keyof T]:T[key]
}

type B = Options1

//配置文件tsconfig.json
//namespace
//声明文件 d.ts
//Mixins混入

1.类装饰器 ClassDecorator target 构造函数
2.属性装饰器 PropertyDecorator
3.参数装饰器 ParameterDecorator
4.方法装饰器 MethodDecorator PropertyDescriptor
5.装饰器工厂
6.import 'reflect-metadata'
7.axios

    const Base = (name:string) => {  
    const fn:ClassDecorator = (target) => {  
        console.log(target)  
        target.prototype.lin = name  //给Http构造函数增加属性  
        target.prototype.fn = () => {     //给Http构造函数增加方法  
            console.log('hello')  
        }  
    }
    return fn
}

const Get = (url:string) => {
    const fn:MethodDecorator = (target,_key,descriptor:PropertyDescriptor) => {
        // const key = Reflect.getMetadata('key',target)
        // axios.get(url).then(res=>{
        //     descriptor.value(key ? res.data[key] : res.data)
        // })
    }
    return fn
}

const Result = () => {
    const fn:ParameterDecorator = (target,key,index) => {
        // Reflect.defineMetadata('key','result',target)
    }
    return fn
}

const Name:PropertyDecorator = (target,key) => {

}

@Base('大林忽悠') //装饰器工厂 闭包 函数柯里化
class Http {
//...
@Name
lin:string
constructor () {
this.lin = '大林忽悠'
}
@Get('https://api...')
getList (@Result() data:any) {
// console.log(data.result.list)
console.log(data)
}
}
const http = new Http() as any

console.log(http.lin)

//Paritial 把某个类型里的属性全部变成可选项
//Required 把某个类型里的属性全部变成必选项
//Readonly 把属性变成只读
//Record<T,K> 将K中所有属性的值转化为T类型
interface PageInfo{
title:string
}
type page = 'a'|'b'|'c'
type mix = Record<PageInfo, page>
const xx:mix = {
'a':{title:'xxx'},
'b':{title:'xxx'},
'c':{title:'xxx'}
}
//Exclude<T,U> 将其中某个类型中属于另一个类型的去掉。
type aa = Exclude<'a'|'b'|'c','a'> //只剩下'b'|'c'
type bb = Exclude<'a'|'b'|'c','a'|'b'> //只剩下'c'
//Extract<T,U>将t,u中的交集提取出来

问题:如何基于一个已有类型,扩展出大部分内容相似,但有部分区别的类型?

通过Pick和Omit
interface test {
name:string,
sex:string,
height:number
}

type Sex = Pick<test, 'sex'>
const aaa:Sex = {sex:'女'}
type WithoutSex = Omit<test, 'sex'>
const b:WithoutSex = {name: '11',height: 160}

ts实现简单发布订阅模式

    //发布订阅模式
interface I {
    events:Map<string,Function[]>
    once:(event:string, callback:Function)=>void  //触发一次
    on:(event:string, callback:Function)=>void  //订阅
    emit:(event:string, ...args:any[])=>void  //派发
    off:(event:string, callback:Function)=>void  //移除
}
class Emitter implements I {
    events: Map<string, Function[]>
    constructor () {
        this.events = new Map()
    }
    once (event:string, callback:Function) {
        //1.创建一个自定义函数,通过on触发一次,触发完就off掉
        const cb = (...args:any[]) => {
            callback(...args)
            this.off(event, cb)
        }
        this.on(event, cb)
    }
    on (event:string, callback:Function) {
        if (this.events.has(event)) {
            //事件存过了
            const callbackList = this.events.get(event)
            callbackList && callbackList.push(callback)
        } else {
            //事件第一次存
            this.events.set(event,[callback])
        }
    }
    emit (event:string, ...args:any[]) {
        const callbackList = this.events.get(event)
        if (callbackList) {
            callbackList.forEach(fn => {
                fn(...args)
            });
        }
    }
    off (event:string, callback:Function) {
        const callbackList = this.events.get(event)
            if (callbackList) {
                callbackList.splice(callbackList.findIndex(callback), 1)
            }
        }
}

const bus = new Emitter()

bus.on('message', (b:boolean, n:number)=>{

})

bus.emit('message', false, 1)