关于前端:你还在认为TypeScirpt-是-AnyScript

45次阅读

共计 17223 个字符,预计需要花费 44 分钟才能阅读完成。

前言

聊聊为何要学习 TypeScript?

  1. 从开发角度来讲, TypeScript 作为强类型语言,对属性有类型束缚。在日常开发中少了缩小了不必要的因参数类型造成的BUG, 当你在应用共事封装好的函数时,类型不晓得怎么传递,在开发TS 时,它会有很敌对类型的提醒,此时你就晓得该函数须要传递哪些参数,并且参数类型是什么类型。
  2. 从我的项目构造来讲, 应用 TypeScript 能够很好的管控我的项目,通过建设各个模块的类型治理,通过interface 或者 类型断言 来治理我的项目中类型,在对应的模块应用类型,当我的项目须要迭代更新时,只须要进行对对应的模块类型进行更改。
  3. 从前端全局观来讲,社区三大框架早已联合 TypeScript 了,社区也很欠缺,曾经过了 该不该学习 TypeScript 的阶段了

去年还有很多敌人犹豫用不必学习 TypeScript , 很多被社区敌人发的文章误导,TypeScript 就是 AnyScript

TypeScript 对没有接触过 强类型语言的敌人有点学习老本,而且在应用 TypeScript 进行开发时,后期可能代码会很臃肿,让你感觉看起来有很多 无用的代码,不过在前期,你就能够感觉到 TypeScript 给你带来的乐趣了。

学会 TypeScript 也对你退职场竞争有点劣势,在跳槽时,如果你曾经应用 TypeScript 联合 框架 做过一些我的项目,面试官也会优先思考你的,薪水从而也晋升了。
前端之路还有很长,新的技术 / 框架更新频率很快,最终还是离不开 JavaScript

上面,咱们一起来看看 TypeScript, 本文是对标 TypeScript 文档进行来解说,更加大白话的简略讲述 如何应用 TypeScript .

动手导图

TypeScript

一,装置环境

#npm install -g typescript

1.1 VSCode 配置主动编译文件

#1. 在目录下  
    tsc --init   主动生成 tsconfig.json 
    tsconfig.json 下 outdir 是输入的门路
#2.  工作 --- 运行工作   监督 tsconfig.json

二,根本语法

2.1 数组

定义应用
// 第一种定义方法   let 数组名:类型[] = []
var arr:number[] = [1,2,3];
console.log(arr);
// 第二种定义方法    let 数组名:Array[类型] = []
var newArr:Array<number> = [1,2,3];
console.log(newArr)

2.2 元组

它示意 曾经 元素的个数和元素类型的数组,各个元素类型能够不一样。

拜访元组长度 和 元素
var strArr:[number,string,boolean] = [22,'测试',false]
console.log(strArr.length)
console.log(strArr[0])
#它只能按类型的优先程序输出内容,否则报错

2.3 枚举 enum

enum类型是对 JavaScript 规范数据类型的一个补充。

  • 如果没有给枚举指定索引的话,默认为 0,通过 枚举对象[索引] 能够获取值
  • 如果指定了枚举索引为字符串的话,通过 枚举. 属性 获取的 它的值
enum Sex {Man,Woman}

let m:Sex = Sex.Man;
console.log(m) //0


let w: string = Sex[1]
console.log(w) //Woman


enum Animal {Dog = 3, Cat, Tiger};

console.log(Animal[5]) //Tiger


enum info {student = '学生', teacher = '老师',  parent = '家长'};

console.log(info.teacher)  // 老师

2.4 任意类型 any

any 为 任意类型,个别在获取 dom 应用

// 任意类型
const newArrs:any = ['测试不同数据',222,false]
console.log(newArrs)
# 输入后果为['测试不同数据', 222, false]
# 应用场景:当你不晓得类型 或 一个对象 或数据 须要多个类型时,应用 any

2.5 undefined 类型

let num:number | undefined ;
console.log(num) // 输入 undefined,然而不会报错
let newNum:number | undefined = 33;
console.log(newNum)  // 输入 33 

2.6 never 类型

never 代表不存在的值类型,罕用作为 抛出异样或者 有限循环的函数返回类型

# 利用场景 
    #1. 抛谬误
    const errDate = (message:string): never => {throw new Error(message)
    }
    #2.  死循环
    const date_for = (): never => {while(true) {}}
    
# never 类型是任意类型的子类型,没有类型是 never 的子类型
别的类型不能赋值给 never 类型,而 never 类型能够赋值给任意类型

2.7 void 类型

void 为 函数没有类型,个别用在没有返回值的函数

# 如果办法类型为 number,则必须返回内容,内容且必须为数字
function add():number{return 2323;}

# 如果办法类型为 void,不须要返回内容
function getAdd():void{console.log('测试')
}

# 如果办法类型为 any,则能够返回任意类型
function getAny():any{return 999 + 'Hello TypeScript'}

console.log(getAny())//999 'Hello TypeScript'

三,类型断言

什么是类型断言?

  • 有时候你在定义一个变量时,起初是不晓得是什么类型,但在应用过程中晓得是什么类型,这时就会用到类型断言了。
3.1 第一种写法 尖括号
const str = '测试'

const resLength : number = (<string>str).length
3.2 第二种写法 as
const str = '测试'

const resLength : number = (str as string).length

四,接口

TypeScript 的外围准则之一是对值所具备的 构造 进行类型查看。

验证类型时,程序不影响验证。

简略的来说,它是类型束缚的定义,当你应用这个定义接口时,它会一一匹对接口中定义的类型。

只有不满足接口中的任何一个属性,都不会通过的。

4.1 接口可选属性

有时候,接口属性不是必须全副须要的,满足某些条件才会须要,这时,就能够采纳可选属性

格局:属性 ?: 类型

interface  Login{
    userName: string,
    password: string,
    auth ?: string
}



function getLogin(obj: Login) {if(obj.auth == '管理员') {console.log('能够查看所有菜单')
    } else {console.log('您的权限比拟低, 目前不能查看')
    }
}


getLogin({
    userName:'zhangsanfeng',
    password: '12121121sd',
    auth: '管理员'
})    // 能够查看所有菜单


getLogin({
    userName:'zhangsanfeng',
    password: '12121121sd'
})    // 您的权限比拟低, 目前不能查看


4.2 接口 只读属性

只读属性:意味着给属性赋值了后,不可扭转。

格局:readonly 属性 : 类型

interface Menus {
    readonly title?:string,
    icon?:string,
    readonly path?:string,
    readonly Layout?:string
}


function getMenuInfo(data:Menus){console.log(data)
    data.icon = '批改图标'   // 能够批改
    // data.path = '/home'    报错,禁止批改,接口属性为只读
    console.log(data)
}


getMenuInfo({
    title: '主页',
    icon:'homes',
    path:'/home',
    Layout: 'Layput'
})
4.3 接口函数类型

用来束缚函数传递参数类型

  • 函数类型的类型查看来说,函数的参数名不须要与接口里定义的名字相匹配。
  • 格局:(参数 1:类型,参数 2:类型):返回值类型
// 获取用户信息
interface UserInfo {(name: string,adress: string,phone: number) : string
}

let getUsefInfo:UserInfo = function(name,adress,phone){return `${name}住在 ${adress}, 手机号为 ${phone}`
}

console.log(getUsefInfo('张锋','天津南开区 xx 小区',188888888))
4.4 接口可索引类型

在定义一个数组时,能够定义一个 索引类型接口,这样就束缚了它必须传递哪些类型的值。

拜访: 通过 变量[索引]

interface Code{[index : number] : string
}

let errCode : Code = ['200 胜利','301 重定向', '400 客户端谬误', '500  服务端出错']

console.log(errCode[3])  //500  服务端出错
4.5 类型接口实现

接口形容了类的公共局部,而不是公共和公有两局部。它不会帮你查看类是否具备某些公有成员。

interface Animals {
    eye: number,
    leg: number,  
}


class Dog  implements Animals {
    eye: number;
    leg: number;
    kind: string
    constructor(eye: number, leg: number, kind: string) {
        this.eye = eye
        this.leg = leg
        this.kind = kind
    }
    getDogInfo(){console.log(` 种类为 ${this.kind}, 有 ${this.eye}只眼,${this.leg}条腿 `)
    }
}

let hashiqi = new Dog(2,4,'哈士奇');
hashiqi.getDogInfo() // 种类为哈士奇, 有 2 只眼,4 条腿
4.6 接口继承(多合一)

接口之间能够相互继承,这样能够更灵便地将接口宰割到可重用的模块里。

interface Shape1 {data: string}

interface Shape2  extends Shape1{
    code: number
    // Shape2 具备 Shape1 的特色
}


class Message implements Shape2 {
    code: number;
    data: string;
    constructor(code : number,data: string) {
        this.code = code;
        this.data = data
    }
    getCodeInfo(){console.log(` 状态码为 ${this.code}, 返回信息为 ${this.data}`)
    }
}


let err = new Message(500,'服务端谬误')
err.getCodeInfo()  // 状态码为 500, 返回信息为服务端谬误
4.7 接口继承类

当接口继承了一个类,那么接口也会领有类的属性和办法。

当别的类 实现这个 接口时,会同时实现 接口的属性和办法,继承类的属性和办法

class Log {
     time: string = '2020-11-2';
     getLog(){console.log('测试')
     }
}

interface  Shape3  extends Log{message : string}



class ErrLog implements Shape3 {
    message: string ;
    time: string;
    constructor(message: string, time: string) {
        this.message = message;
        this.time = time
    }
    getLog(): void {console.log("Method not implemented.");
    }
}

let errs = new ErrLog('测试','2020-11-2')
errs.getLog()  //Method not implemented.

五,泛型

接触过 JAVA 的同学,应该对这个不生疏,十分熟了。

作为前端的咱们,可能第一 次听这个概念。通过 字面意思能够看出,它指代的类型比拟宽泛。

  • 作用:: 防止反复代码,代码冗余

然而它和 any 类型 还是有区别的。

  • any 类型:如果一个函数类型为 any,那么它的参数能够是任意类型,个别传入的类型与返回的类型应该是雷同的。如果传入了一个 string 类型的参数,那么咱们也不晓得它返回啥类型。
  • 泛型:它能够使 返回类型 和 传入类型 保持一致,这样咱们能够分明的晓得函数返回的类型为什么类型。
5.1 泛型接口

泛型接口能够这样了解:

当你须要给接口指定类型时,但目前不晓得属性类型为什么时,就能够采纳泛型接口

你能够给接口指定参数为多个泛型类型,也能够单个;当应用时,明确参数类型即可。

 interface User <T,S,Y> {
     name: T;
     hobby: S;
     age: Y;
 }

 class People implements User<String,String,Number> {
     name: String;
     hobby: String;
     age: Number;
     constructor(name:string,hobby:string,age:number){
         this.name = name;
         this.hobby = hobby;
         this.age = age; 
     }
    getInfo(){console.log(this.name+"------------------"+this.hobby)
        console.log(`${this.name}的年龄为 ${this.age}`)
    }  
 }
 let xiaoZhou =  new People('小周','敲代码',22)
 xiaoZhou.getInfo() 
 // 小周 ------------------ 敲代码
//  小周的年龄为 22
5.2 泛型函数

定义泛型函数,能够让 传入参数类型参数 和 返回值类型保持一致。

泛型 标记个别用字母大写,T 能够随便更换

格局:函数名 <T> (参数 1:T) : 返回值类型 T
function genericity<T> (data: T) : T {console.log(data)
    return data
}

genericity("测试")
genericity(666)
genericity(['前端','后端','云端'])

5.3 泛型类
  1. 什么是泛型类

它规定了类中属性和办法的 类型,而且必须和类型定义的类型保持一致。

  1. 泛型类的作用

能够帮忙咱们确认类的所有属性都在应用雷同的类型

  1. 应用格局
class 类名 <T> {
 name!: T;
 hobby!: T;
}

# 这样这个类的所有类型为 number
let 实例 =  new 类名 <number>();
class GenericityA<X>{
    sex!: X;
    age!: X;
}


let gen = new GenericityA<number>();

// gen.sex = '测试'   报错
gen.age = 3
console.log(gen.age)
5.4 泛型束缚
  1. 接口束缚
  • 通过定义接口,泛型函数继承接口,则参数必须实现接口中的属性,这样就达到了泛型函数的束缚
  1. 类束缚
  • 通过给类的泛型指定为另一个类,这样就规定了类泛型的类型都为另一个类
# 第一种
// 定义接口
 interface DataInfo{
     title: string,
     price: number
 }


//  泛型函数 继承接口,进行对参数类型束缚, 如果传入的参数中,没有蕴含接口属性,则编译不通过
 function getDataInfos< T extends DataInfo> (obj: T) : T {return obj}

 let book = {
     title: '前端进阶',
     price: 50,  
     author: '小新'
 }

 console.log(getDataInfos(book)) //{title: '前端进阶', price: 50, author: '小新'}
# 第二种
//  通过类来束缚
 class Login{
    username: string;
    password: string;
    constructor(username: string,password:string){
        this.username = username
        this.password = password
    }
 }

class Mysql<T>{login<T>(info:T):T{return info}
}

let  x = new Login('admin','12345');
let  mysql = new Mysql<Login>();
console.log(mysql.login(x)) //Login {username: 'admin', password: '12345'}

<hr/>

六,类 Class

说到类,做后端的敌人应该都理解,前端 在 ES6 中,才呈现了 类 Class 这个关键词。

Class 有哪些特色

  • 属性
  • 结构器
  • 办法
  • 继承 extends
  • 属性 / 办法 修饰符
  • 动态属性
  • 抽象类
  • 存取器 getters/setters

6.1 修饰符

public 共有的

当属性 / 办法 修饰符为 public 时,如果后面没有,默认会加上,咱们能够自在的拜访程序里定义的成员。

class Fruit {
    public name: string;
    price: number;
    // 以上为等价
    constructor(name: string, price: number) {
        this.name = name;
        this.price = price
    }
    getFruitInfo(){console.log(` 您要购买的水果为 ${name}, 价格为 ${this.price}`)
    }
}
private 公有的

当成员被标记成 private时,它就不能在申明它的类的内部拜访。

class Fruit {
    public name: string;
    private price: number;

    // 以上为等价
    constructor(name: string, price: number) {
        this.name = name;
        this.price = price
    }
    getFruitInfo(){console.log(` 您要购买的水果为 ${name}, 价格为 ${this.price}`)
    }
}

const apple = new Fruit('苹果',22)
// console.log(apple.price)   报错,实例不能够拜访公有属性
protected 受爱护的

protected修饰符与 private修饰符的行为很类似,但有一点不同,protected成员在派生类中依然能够拜访, 不能够通过实例来拜访受爱护的属性。

class A {
    protected name : string;
    protected age : number;
    constructor(name: string , age: number) {
        this.name = name;
        this.age = age
    }
    getA(){console.log('A')
    }
}

class B extends A {
    protected job : string;
    constructor(name: string, job: string,age: number) {super(name,age)
        this.job = job
    }
    getB(){console.log(`B 姓名为 ${this.name} && 年龄为 ${this.age} && 职业为 ${this.job},`)
    }
}

let b = new B('小飞','前端工程师',22)
b.getA()  //A
b.getB()   //B 姓名为小飞 && 年龄为 22 && 职业为前端工程师,
// console.log(b.name)  报错,拜访不了,protected 成员只能在派生类中能够拜访,不能通过实例来拜访。

6.2 动态属性

类的动态成员(属性 和 办法) 只能通过 类来能够拜访。

定义:static 属性 / static 办法

class Food {
    public name: string;
    private price: number;
    static adress: string = '四川';
    // 以上为等价
    constructor(name: string, price: number) {
        this.name = name;
        this.price = price
    }
    getFruitInfo(){console.log(` 您要购买的货色为 ${name}, 价格为 ${this.price}`)
    }
}

const spicy = new Food('辣条',3)

console.log(Food.adress)  // 四川
// console.log(spicy.adress)  报错  类的实例对象不能够拜访 类的动态属性。只能够通过类. 属性来拜访

6.3 继承 extend

继承的本意很好了解,当子类继承了父类,那么子类就领有了父类的特色(属性) 和 行为(办法),

class T {
    name:string;
    constructor(name:string){this.name = name}
    getNames(){console.log('继承类 T')
    }
}

class S extends T {constructor(name:string){
        // 派生类领有 T 属性和办法
        super(name)
    }
    getName(){console.log(this.name)
    }
}

let  ss = new S('测试继承')
ss.getName()  
ss.getNames()
// 测试继承
// 继承类 T 

6.4 抽象类

  • 抽象类能够蕴含成员的实现细节。abstract关键字是用于定义抽象类和在抽象类外部定义形象办法。
  • 抽象类中的形象办法不蕴含具体实现并且必须在派生类中实现。
abstract class E{
    abstract name: string;
    abstract speak():void;
    abstract play():void;}


class F implements E {
    name: string;
    constructor(name:string){this.name = name}
    //  派生类 F 必须实现 抽象类 E 的办法和属性 
    speak(): void {console.log('具备聊天性能')
    }
    play(): void {console.log('具备娱乐性能')
    }
    get(){console.log(this.name)
    }
}


let f = new F('测试');
f.play() // 具备娱乐性能
f.get() // 测试
f.speak()  // 具备聊天性能

七,TS 中的函数

函数类型包含 参数类型 和 返回值类型

7.1 函数增加返回值类型

每个参数增加类型之后再为函数自身增加返回值类型.

TypeScript 可能依据返回语句主动推断出返回值类型,因而咱们通常省略它。

上面会介绍在 TS 中,两种写函数的格局

//  第一种写法
let getInterFaceInfo : (obj:object) => void = function(obj){console.log(obj)
}

let infos: object = {
    code: 200,
    message: '发送胜利'
}
getInterFaceInfo(infos)


//  第二种写法
function getCode(code: number, message:string) : void {console.log(`code 为 ${code},message 为 ${message}`)
}

getCode(200,'承受胜利')    
7.2 函数可选参数 / 默认参数

JavaScript 里,每个参数都是可选的,可传可不传。没传参的时候,它的值就是 undefined。

在 TypeScript 里咱们能够在参数名旁应用 ?实现可选参数的性能。

  • 可选参数必须放在必须参数前面。
格局:函数名(变量名?:类型): 类型 {}  
  • 默认参数,在传递参数时,指定默认值
格局:函数名(变量名:类型 = "xx"): 类型 {}  
//  可选参数
function getNetWork(ip:string,domain:string,agreement?:string){console.log(`ip 地址为:${ip}, 域名为 ${domain}, 协定为 ${agreement}`)
}

getNetWork('127.0.0.1','www.xiaomi.com')  //ip 地址为:127.0.0.1, 域名为 www.xiaomi.com, 协定为 undefined

// 默认参数
function getNetWorks(ip:string,domain:string,agreement:string = 'http'){console.log(`ip 地址为:${ip}, 域名为 ${domain}, 协定为 ${agreement}`)
}
getNetWorks('127.0.0.1','www.xiaomi.com') //ip 地址为:127.0.0.1, 域名为 www.xiaomi.com, 协定为 http    
7.3 函数残余参数

有时,你想同时操作多个参数,或者你并不知道会有多少参数传递进来。

在 JavaScript 里,你能够应用 arguments来拜访所有传入的参数。

在 TypeScript 中,能够把所有参数集中在一个变量中,后面加上... 示意 残余参数。

留神

  • 间接通过变量拜访
  • 也能够通过索引拜访
  • 只能定义一个残余参数,且地位在 默认参数和可选参数前面
function getNumberInfo(num:number,...peopleArray: string []) {console.log(` 人员个数为 ${num}, 成员为 ${peopleArray}`)  // 也能够通过索引来获取元素
    console.log(` 人员个数为 ${num}, 成员为 ${peopleArray[1]}`) 
}

getNumberInfo(4,'小明','小李','小红','小张')  
// 人员个数为 4, 成员为小明, 小李, 小红, 小张
// 人员个数为 4, 成员为小李

<hr/>

八,枚举

枚举能够清晰地表白一组对应关系。

TypeScript 反对数字的和基于字符串的枚举。

8.1 数字枚举

默认枚举的程序以 0 结尾,而后主动递增。

枚举程序也能够指定 值,指定后,它后面第一个还是以 0 递增

拜访

  • 通过 枚举名. 属性 拜访到的是 序号
  • 通过 枚举名 [序号] 拜访到的是 属性名
enum Sex {
    x,
    man = 4,
    woman 
}

console.log(Sex.x)   //0
console.log(` 小红的性别为 ${Sex[5]}`) // 小红的性别为 woman
console.log(` 后端承受小红的性别 ID ${Sex.woman}`) // 后端承受小红的性别 ID 5
8.2 字符串枚举
enum Job {
    frontEnd = '前端',
    backEnd = '后端'
}

console.log(Job) //{frontEnd: '前端', backEnd: '后端'}    

九,高级类型

9.1 穿插类型

它指 能够将多个类型合并为一个类型。标识符为 & , 当指定一个变量类型为 穿插类型时,那么它领有穿插类型的所有属性,也就是并集。

interface DonInterface {run():void;
}
interface CatInterface {jump():void;
}
// 这里的 pet 将两个类型合并, 所以 pet 必须爱护两个类型所定义的办法
let pet : DonInterface & CatInterface = {run:function(){},
    jump:function(){}
}
9.2 联结类型
  • 联结类型示意一个值能够是几种类型之一。
  • 用竖线(|)分隔每个类型。
  • 一个值是联结类型,咱们只能拜访此联结类型的所有类型里共有的成员。
 function getMenus(info: string | number) {console.log(info)
 }


getMenus("测试")
getMenus(2)
// getMenus(false)  报错

十,模块

模块:定义的变量,函数,类等等,只能在本身的作用域里应用。如果想在内部拜访应用,那么必须应用export 将其导出即可。

应用模块:通过 import 将模块内容导入即可应用。

  • 模块是自申明的;两个模块之间的关系是通过在文件级别上应用 imports 和 exports 建设的。
  • 模块应用模块加载器去导入其它的模块。在运行时,模块加载器的作用是在执行此模块代码前去查找并执行这个模块的所有依赖。

10. 导出

10.1 导出申明

任何申明(比方变量,函数,类,类型别名或接口)都可能通过增加 export 关键字来导出。

导出能够对任何申明 进行重命名,避免命名抵触,通过 as 来批改

# 模块 A 文件

// 导出接口
 export interface A {getList() : void
 }

//  导出变量
export const  GET_METHOD =  "get"
 

//  导出类
export class S implements A {getList(): void {console.log("导出类")
    }
    
}

function getQueryData():void {console.log("获取分页数据")
}


// 导出模块 变量重命名
export {getQueryData as getQuery}



# 文件 B
import {getQuery,S,A} from './ 模块 A';

// 应用模块中的函数
getQuery()

// 实例模块中类的对象
const a = new S();
a.getList()  // 输入导出类

// 实现模块中的 A 接口
class Y implements A {getList(): void {throw new Error('Method not implemented.');
    }
    
}
10.2 组合模块应用

通常一个大的模块是多个子模块组成的。那么咱们能够通过 在大的模块中导入多个子模块。

格局: export * from "模块"

应用组合模块: import * as 重命名变量 from‘组合模块门路’

# 模块 C
    //  导出变量
    export const  GET_METHOD =  "get"
# 模块 B

export const str: string = "B 模块"

export  function getContent():void{console.log("我是模块 B 的内容")
}
# 组合模块


 const  res : object =  {
    code: 200,
    message: "申请胜利"
 }


export function getRes(): void {console.log(res)
 }

 
 # 导出子模块
 export * from "./modulesC"
 export * from "./moduleB"
10.3 应用组合模块
import * as T from "./modulesA";


// C 模块中的
console.log(T.GET_METHOD)


// B 模块中的内容
console.log(T.str)  // B 模块
T.getContent() // 我是模块 B 的内容


// A 模块中的内容
T.getRes() //{ code: 200, message: '申请胜利'} 
10.4 默认导出

每个模块都能够有一个 default 导出。默认导出应用 default关键字标记;并且一个模块只可能有一个 default 导出。

# 模块

export interface K {
    name:string;
    birth:string;
}


export default class Student implements K {
    name: string;
    birth: string;
    constructor(name:string,birth:string){
        this.name = name;
        this.birth = birth;
    } 
    getStudentInfo(){console.log(this.name+this.birth)
    }
}
# 文件 A
import D,{K} from './modulesD'


//  应用默认导出
 const d = new D('小明','1998')
 d.getStudentInfo()


// 参数类型为接口 K 
 function getMessage(obj: K): void {console.log(obj)
 }
 let obj = {
     name:"小红",
     birth: "1998"
 }
 getMessage(obj);

10.5 export = 和 import = require()

CommonJS 和 AMD 的环境里都有一个 exports 变量,这个变量蕴含了一个模块的所有导出内容。

CommonJS 和 AMD 的 exports 都能够被赋值为一个 对象

exports 和 export default 用处一样,然而 export default 语法不能兼容 CommonJS 和 AMD 的exports

在 TypeScript 中,为了达到这样成果,能够这样写:

导出:export = 等于 exports

导入:import module = require("module")

# 模块
// 相当于默认导出
export = class Mongodbs{
    host:string;
    user:string;
    password:string;
    port:number;
    databaseName:string;
    constructor(host:string,user:string,password:string,port:number,databaseName:string) {
        this.host = host;
        this.user = user;
        this.password = password;
        this.port = port;
        this.databaseName = databaseName
    }
    query(table:string){console.log(`select * from ${table}`)
    }
}
# 应用模块

import MogoDb = require("./modulesE")  

const mogodb = new MogoDb('1270.0.1','admin','123456',3006,'TypeScript')

mogodb.query('Vue') //select * from Vue

十一,命名空间

  1. 定义
  • “外部模块”称为“命名空间”
  • “内部模块”称为“模块”
  1. 作用
  • 缩小命名抵触,将代码组织到一个空间内,便于拜访。
  1. 应用格局
  • 通过 namespace 空间名 {} ,外部通过 export 导出来应用外部成员
namespace XiaoXin {
    export interface  GetData{
        name: string;
        price: number;
        getInfo(obj:object):any;
    }
    export interface  GetMessage {
        code: number;
        message: string;
    }

    export class Book implements  GetData{
        name: string;
        price: number;
        constructor(name:string,price:number){
            this.name = name;
            this.price = price
        }
        getInfo(obj: object) {throw new Error("Method not implemented.");
        }
        buyBook(obj: GetMessage) {console.log(obj)
        }
    }           
}


const fontEnd = new  XiaoXin.Book("前端开发手册",99)

var obj = {
    code: 200,
    message:"购买胜利"
}

fontEnd.buyBook(obj)  //{code: 200, message: '购买胜利'}


function test(obj:XiaoXin.GetMessage){console.log(obj)
}

test(obj)  //{code: 200, message: '购买胜利'}    

11.1 拆分命名空间

当利用变得越来越大时,咱们须要将代码拆散到不同的文件中以便于保护。

咱们能够将命名空间文件拆分成多个文件,然而它们的命名空间名还是应用的同一个,各个文件相互依赖应用。然而必须文件最结尾引入 命名空间文件。

格局: /// <reference path="MS1.ts"/>

# 根命名空间
namespace School {export const schoolName =  "清华大学"}
# 子命名空间 1
/// <reference path="MS1.ts" />

namespace School{
    export class Teacher {
        faculty:string;
        name:string;
        age:number;
        constructor(faculty:string,name:string,age:number){
            this.faculty = faculty;
            this.name = name;
            this.age = age
        }
        getInfo(){console.log(`${this.name}为 ${this.faculty}, 年龄为 ${this.age}`)
        }
        getSchool(schoole:string){console.log(`${this.name}老师就任于 ${schoole}`)
        }
    }
}
#  子命名空间 2
///  <reference path="MS1.ts" />

 namespace School{
     export class Student{
         name:string;
         age:number;
         hobby:string;
         constructor(name:string,age:number,hobby:string) {
             this.name = name;
             this.age = age;
             this.hobby = hobby;
         }
         getInfo(){console.log(`${this.name}是一个学生,年龄为 ${this.age}, 喜好是 ${this.hobby}`)
         }
     }
 }
# 应用合并的命名空间


导入命名空间
/// <reference path="MS1.ts" />
/// <reference path="MS2.ts" />
/// <reference path="MS4.ts" />



let teacher = new School.Teacher('计算机传授','张博士',34);
teacher.getInfo() // 张博士为计算机传授, 年龄为 34
teacher.getSchool(School.schoolName)  // 张博士老师就任于清华大学


let students = new School.Student('张三',17,'玩 LOL');
students.getInfo()  // 张三是一个学生,年龄为 17, 喜好是玩 LOL

编译命名空间文件
第一种办法:会编译为 一个 js 文件
tsc --outFile sample.js Test.ts


第二种办法:会编译为多个 js 文件,而后通过 <script> src 引入 js 文件即可
tsc --outFile sample.js Validation.ts LettersOnlyValidator.ts ZipCodeValidator.ts Test.ts

十二,装璜器

装璜器是一种非凡类型的申明,它可能附加到类申明、办法、拜访符、属性、类办法的参数上,以达到扩大类的行为。

自从 ES2015 引入 class,当咱们须要在多个不同的类之间共享或者扩大一些办法或行为的时候,代码会变得盘根错节,极其不优雅,这也是装璜器被提出的一个很重要的起因。

12.1 润饰器分类
  • 类装璜器
  • 属性装璜器
  • 办法装璜器
  • 参数装璜器
润饰器写法:1. 一般润饰器(不传参数)2.  装璜器工厂 (传参数)
12.2 类装璜器

类装璜器表达式会在运行时当作函数被调用,类的构造函数作为其惟一的参数。

应用场景:利用于类构造函数,能够用来监督,批改或替换类定义。

const extension = (constructor: Function):any => {
    constructor.prototype.coreHour = '10:00-15:00'
  
    constructor.prototype.meeting = () => {console.log('重载:Daily meeting!');
    }

  }

@extension
class Employee {
  public name!: string
  public department!: string


  constructor(name: string, department: string) {
    this.name = name
    this.department = department
  }

  meeting() {console.log('Every Monday!')
  }

}

let e: any = new Employee('装璜器', '测试')
console.log(e)  //Employee {name: '装璜器', department: '测试'}
 console.log(e.coreHour) // 10:00-15:00
 e.meeting()             // 重载:Daily meeting!
  
12.3 类属性装璜器

作用于类属性的装璜器表达式会在运行时当作函数被调用,传入下列 3 个参数 targetnamedescriptor

  • target: 对于动态成员来说是类的构造函数,对于实例成员是类的原型对象
  • name: 成员的名字
  • descriptor: 成员的属性描述符

执行程序:当调用有装璜器的函数时,会先执行装璜器,后再执行函数。

通过润饰器实现一个属性只读性能,其实就是批改数据描述符中的 writable 的值:

function readonly(value: boolean){return function(target:any,name:string,descriptor:PropertyDescriptor) {descriptor.writable = value}
}


class Student{
    name:string;
    school:string = '社会大学'
    constructor(name:string) {this.name = name}
    @readonly(false)
    getDataInfo(){console.log(`${this.name}毕业于 ${this.school}`)
    }
}

let sss = new Student('小李子')
// 报错,  只能读, 不能批改
// sss.getDataInfo = () => {//     console.log("测试批改")
// }

sss.getDataInfo()

往期精彩文章

  • Vue 组件通信的 8 种形式
  • Vue3 + TypeScript 开发实际总结
  • Vue 权限路由 + 数据权限 最佳实际

最初

如果喜爱的话能够点赞👍👍👍关注,反对一下,心愿大家能够看完本文有所播种!

欢送关注公众号: 前端自学社区

正文完
 0