前言
聊聊为何要学习TypeScript?
- 从开发角度来讲,
TypeScript
作为强类型语言,对属性有类型束缚。在日常开发中少了缩小了不必要的因参数类型造成的BUG
,当你在应用共事封装好的函数时,类型不晓得怎么传递,在开发TS
时,它会有很敌对类型的提醒,此时你就晓得该函数须要传递哪些参数,并且参数类型是什么类型。 - 从我的项目构造来讲, 应用
TypeScript
能够很好的管控我的项目,通过建设各个模块的类型治理,通过interface
或者类型断言
来治理我的项目中类型,在对应的模块应用类型,当我的项目须要迭代更新时,只须要进行对对应的模块类型进行更改。 - 从前端全局观来讲,社区三大框架早已联合
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) //0let w: string = Sex[1]console.log(w) //Womanenum Animal {Dog = 3, Cat, Tiger};console.log(Animal[5]) //Tigerenum 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 泛型类
- 什么是泛型类
它规定了类中属性和办法的 类型,而且必须和类型定义的类型保持一致。
- 泛型类的作用
能够帮忙咱们确认类的所有属性都在应用雷同的类型
- 应用格局
class 类名<T> { name!: T; hobby!: T;}# 这样这个类的所有类型为 numberlet 实例 = new 类名<number>();
class GenericityA<X>{ sex!: X; age!: X;}let gen = new GenericityA<number>();// gen.sex = '测试' 报错gen.age = 3console.log(gen.age)
5.4 泛型束缚
- 接口束缚
- 通过定义接口, 泛型函数继承接口,则参数必须实现接口中的属性,这样就达到了泛型函数的束缚
- 类束缚
- 通过给类的泛型指定为另一个类,这样就规定了类泛型的类型都为另一个类
# 第一种// 定义接口 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() //Ab.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) //0console.log(`小红的性别为${Sex[5]}`) //小红的性别为womanconsole.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}# 文件Bimport {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"
# 模块Bexport 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) }}
#文件Aimport 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
十一, 命名空间
- 定义
- “外部模块”称为“命名空间”
- “内部模块”称为“模块”
- 作用
- 缩小命名抵触,将代码组织到一个空间内,便于拜访。
- 应用格局
- 通过
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() //张博士为计算机传授,年龄为34teacher.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!'); } }@extensionclass 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个参数
target
、name
、descriptor
:
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 权限路由+数据权限 最佳实际
最初
如果喜爱的话能够点赞关注,反对一下,心愿大家能够看完本文有所播种 !
欢送关注公众号: 前端自学社区