# typescript **Repository Path**: front-end-learn/typescript ## Basic Information - **Project Name**: typescript - **Description**: TS 入门学习 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2020-08-24 - **Last Updated**: 2020-12-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README #### 开发工具中配置typescirpt自动编译 ##### vscode: 创建tsconfig.json文件 tsc --init 生成配置文件 tsconfig.json配置文件中,修改outDir配置项,取消注释然后修改为.js vscode中,点击上方栏位run task,选择ts监听 完成 TS类型 ##### 与es5中的区别 // es5:类型变化不报错 var flag = true; flag = 234; // ts:必须指定类型 typescript var flag:boolean=true; flag = 131;//报错 ##### TS类型: 1.boolean 2.number 3.string 4.array数组: 方式1:var arr:number[] = [1,2,3]//制定arr里面全是数字 方式2:var arr:Array= [1,2,3] 5.元组类型(tuple) 方式1:属于数组的一种,即数组中每一个元素指定类型 方式2:var arr:[number, string]=[123,“this is ts”]; 6.枚举类型(enum) ``` // 常用来标识状态码 enum Flag{ success=1, error=2 } let f:Flag=Flag.error; console.log(f);// 2 // 如果 没有标识符没有赋值,那么打印的就是下标 enum Color{blue,red,orange}; var c:Color=Color.red; console.log(c); //1,下标 enum Color{blue,red=3,orange}; var c:Color=Color.red; console.log(c); //3 // 常用来标识状态码 enum Err{ 'undefined'=-1, 'null'=-2, } var c:Err=Err.null console.log(c) // -2 ``` ##### 7.任意类型any 类似ES5不指定变量类型的var var num:any=123; num = true;// 不报错 ##### 8.null类型和undefined其他数据类型的子类型 变量定义之后没有赋值,报undefined // 一个元素可能是number类型,可能是null或者undefined var num:number | undefined | null; ##### 9.void,和java一样 没有返回值类型 // 如果方法没有返回值 function run():void{ console.log('asdf') } // 如果方法有返回值: function run():number{ return 1; } ##### 10.never类型,代表从来不会出现的值,是其他类型(包括null‘和undefined)的子类型,代表从不会出现的值 自己理解为上述类型之外的数据类型 ``` // 如下,接收Err类型的数据 var a:never; a = undefined;//报错 a = (()=>{ new Throw Err("报错") })() ``` #### 函数的定义 ##### ES5中 ``` // 函数声明法 function run(){ return ... } //匿名函数 var run2 = function(){ return .. } ``` TS中: ``` //函数声明法 function run():number{ return 123; } // 匿名函数 var fun2=function():number{ return 123; } ``` ts中定义方法传参 ``` function getInfo(name:string, age:number):string{ return name + " " + age; } var getInfo= function(name:string, age:number):string{ return name+age; } ``` 方法可选参数 // es5里方法实参和形参可以不一样,但是ts必须一致,如果不一样就需要配置可选参数 参数后边加?可以设置参数可选传 可选参数必须配置到参数的最后边 ``` function getInfo(name:string, age?number):string{ return … } ``` 默认参数 // 默认参数,直接在形参赋值 function getInfo(name:string, age:number=20):string{ return… } 剩余参数 function sum(a:number, b:number, c:number, d:number):number{ return a+b+c+d; } // 三点运算符:接收不固定参数的(剩余参数)的值 function sum(…rest:number[]):number{ var sum= 0 ; for(var i=0; i = ['123', '222']; // 对数组的约束,数组类型接口 interface UserArray{ // 表示数组中index必须是number,value必须是string [index:numer]:string; } var arr:UserArray=['123', '22312']; // 对对象的约束,对象类型接口 interface UserObj{ [index:string]:string; } var obj:UserObj={name:"2342"}; // 对类的约束,类类型接口,和抽象类有点相似 interface Animal{ // 规定实现类必须要有name属性和eat方法 name:string; eat(str:string):void; } class Dog implements Animal{ name:string;// 若没此属性,ts会编译报错 constructor(name:string){ this.name = name; } eat(){ log("eat") } } // 接口的扩展:接口可以继承接口 interface Animal{ eat():void; } interface Person extends Animal{ work():void; } class Web implements Person{ public name:string; constructor(name:string){ this.name = name; } // eat必须定义 eat(){ log(this.name+"吃") } // work也必须定义 work(){ log(this.name+"工作") } } interface Animal{ eat():void; } interface Person extends Animal{ work():void; } class Programmer{ 构造方法省略 coding(code:string){ log(this.name+ " "+code) } } class Web extends Programmer implements Person{ public name:string; constructor(name:string){ this.name = name; } // eat必须定义 eat(){ log(this.name+"吃") } // work也必须定义 work(){ log(this.name+"工作") } } ##### 泛型 和any有什么区别? any放弃了类型检查 如果想做到传入什么类型就返回什么类型,例如传入number就返回number,这时候就可以使用泛型 ``` function getData(value:any):any{ return ""//什么类型都可以 } ``` 泛型: 软件工程中,我们不仅要创建一致的定义好的API,同时也要考虑可重用性,组件不仅能够支持当前的数据类型,同时也能支持未来的数据类型,这在创建大型系统时为你提供了十分灵活的功能 在像c#和java中,可以使用泛型来创建可重用的组件,一个组件可支持多种类型的数据,这样用户就可以以自己的数据类型来使用组件 通俗理解:泛型就是解决 类 接口 方法的重用性、以及对不特定数据类型的支持 可以支持不特定的数据类型 function getData(value:T):T{ return value;//传入什么返回什么 } // 这样调用 getData(123123); getData("12131"); // 也可以写成: function getData(value:T):any{ return value;//传入什么返回什么 } 泛型类,比如有个最小堆算法,需要同时支持返回数字和字符串两种类型,通过类的泛型来实现,示例: // 定义泛型类 class MinClass{ public list:number[]=[]; add(num:number){ this.list.push(num); } min():number{ var minNum = this.list[0]; for(var i = 0;ithis.list[i]){ minNum=this.list[i]; } } return minNum; } } // 调用 var m = new MinClass(); m.add(3); m.add(2); log(m.min());// 2 但是上边的只能传入数字类型,是否可以用泛型解决?可以: class MinClass{ public list:T[]=[]; add(num:T):void{ this.list.push(num); } min():T{ var minNum = this.list[0]; for(var i = 0;ithis.list[i]){ minNum=this.list[i]; } } return minNum; } } // 调用,实例化时候要先声明参数类型(); m1.add(2); m1.add(4); log(m.min());// 2 函数类型接口 指定特殊类型的方法: interface ConfigFn{ (value1:string, value2:string):string; } var setData:ConfigFn=function(value1:string, value2:string):string{ return value1 + value2; } setData("name", "张三); 泛型接口写法1: interface Config{ (value:T):T; } var getData:ConfigFn=function(value:T):T{ return value; } getData("张三"); 泛型接口写法2: interface Config{ (value:T):T; } function getData(value:T):T{ return value; } var myGetData:ConfigFn=getData; myGetData("张三"); // 把类作为参数来约束数据传入的类型 class User{ username:string | undefined; password:string | undefined; } class MySqlDb{ add(user:User):boolean{ console.log(user); retrun true; } } // 调用 var u = new User(); u.username="张三"; u.password="123456"; var Db = new MySqlDb(); Db.add(u);// console.log(u) // 上述方法可以改为泛型类 // 操作数据库的泛型类,这样可以规范插入数据库数据的类规范 class MySqlDb{ add(info:T):boolean{ log(info); return true; } } // 想给User表增加数据 // 1、定义一个User类 和数据库进行映射 class User{ username:string | undefined; password:string | undefined; } var u = new User(); u.username= '张三'; u.password="2312"; var Db = new MySqlDb();// 这一步很关键,要定义User类型 Db.add(u); // 2、文章类,数据库映射 class Article{ title:string | undefined; desc:string | undefined; status:number | undefined; constructor(params:{ title:string | undefined; desc:string | undefined; status?number | undefined;// status可选参数 }){ this.title=params.title; this.desc=params.desc; this.status=params.status; } } // 调用 var a = new Article({ title:"分类", desc:"111", status:1 }) //类当前参数的泛型类 var Db = MySqlDB
();// 指定类型 Db.add(a);// log a 实战:要实现TS封装统一操作Mysql Mongodb Mssql的底层库 // 先定义一个接口,用于提供各类型数据库规范 interface DBI{ add(info:T):boolean; update(info:T, id:number):boolean; delete(id:number):boolean; get(id:number):any[]; } // 定义一个操作mysql的类,注意 要实现泛型接口 这个类也应该一定是个泛型类 class MysqlDb implements DBI{ add(info:T): boolean{ log(info); } update... delete... get... } // 调用 操作数据表,定义一个User类和数据库进行映射,并进行MySql数据的插入操作 class User{ username:string | undefined; password:string | undefined; } var u = new User(); u.username = "张三"; u.password="213"; var oMysql = new MysqlDb();// 声明User类型参数 oMysql.add(u);// 插入 ##### 模块 概念: 把一些公共的功能单独抽离成一个文件作为一个模块 模块里面的变量 函数 类等默认都是私有的,如果我们要在外部访问模块内的数据(函数、变量、类) 我们就需要通过export暴露模块里面的数据 然后其他地方通过import引入模块就可以使用模块内的数据 模块暴露export: // 方式一 export function a(){ ... } // 方式二 function a(){ ... } export { a } 模块导入import: import { a, a as alias } from "xxx"; a(); alias(); 模块默认导出default,一个模块只能用一次 暴露: export default a(){ } 引入(不用花括号): import a from "aaa"; a(); DB库用模块化封装// 省略了,代码比较简单,可以参考这里 ts命名空间 内部模块,主要用于组织代码,避免命名冲突, 个人理解:模块之中再分模块 定义模块、并导出不同命名空间: export namespace A{ interface Animal{ name: string; eat(): void; } export Class Dog implements Animal{ name: string; constructor(name:string){ this.name = name; } eat:void(){ log(this.name +"在空间A中吃狗粮") } } } export namespace B{ interface Animal{ name: string; eat(): void; } export Class Dog implements Animal{ name: string; constructor(name:string){ this.name = name; } eat:void(){ log(this.name +"在空间A中吃狗粮") } } } 调用: import {A, B} from "xxx"; var d = new A.Dog("小黑"); d.eat();// 小黑在空间A中吃狗粮 var dog = new B.Dog("小花"); dog.eat(); // 小花在空间B中吃狗粮 ##### 装饰器 定义: 装饰器是一种特殊类型的声明,他能够被附加到类声明,方法,属性或者参数上,可以修改类的行为。 通俗的将装饰器就是一个方法,可以注入到类、方法、属性参数上来扩展类、属性、方法、参数的功能。 常见的装饰器有:类装饰器、属性装饰器、方法装饰器、参数装饰器 装饰器的写法:普通装饰器(无法传参)、装饰器工厂(可传参) 装饰器是过去几年中js最大的成就之一,已经是ES7的标准特性之一 类装饰器:普通装饰器 function logClass(params:any){ console.log(params); // params就是当前类 params.prototype.apiUrl = "动态扩展的属性"; params.prototype.run=function(){ console.log("我是一个run方法"); } } @logClass // 类装饰器,普通装饰器,无法传参,默认吧class传入 class HttpClient{ constructor(){ } getData(){ } } 类装饰器:装饰器工厂 ##### 作用: 修改构造函数 扩展类属性和方法 定义: function logClass(params:string){// params是下方传过来的参数 return function(target:any){// target相当于是默认传过来的 log(target); log(params); target.prototype.apiUrl = params; } } @logClass("https://baidu.com")// 可以传参 class HttpClient{ constructor(){ } getData(){ } } var http:any = new HttpClient(); console.log(http.apiUrl);// https://baidu.com 可以修改构造函数的写法 function logClass(target:any){ log(target); return class extends target{ apiUrl:any = "我是修改后的新数据"; getData(){ this.apiUrl = this.apiUrl + "----"; log(this.apiUrl); } } } @logClass class HttpClient{ public apiUrl:string | undefined; constructor(){ this.apiUrl = "我是构造函数里面的apiUrl" } getData(){ log(this.apiUrl) } var http= new HttpClient(); http.getData(); ###### 属性装饰器 作用: 可以给属性赋值 // 类装饰器 function logClass(params:string){// params是下方传过来的参数 return function(target:any){// target相当于是默认传过来的 log(target); log(params); target.prototype.apiUrl = params; } } // 属性装饰器 function logProperty(params:any){ // 固定写法,参数中,target为类对象,attr为参数名称 return function(target:any, attr:any){ log(target); log(attr); target[attr] = params; } } @logClass("https://baidu.com")// 可以传参 class HttpClient{ // 这个属性修饰器的作用就是给url赋值初始值 @logProperty("http://baidu.com") public url:any | undefined; constructor(){ } getData(){ } } var http:any = new HttpClient(); console.log(http.apiUrl);// https://baidu.com ###### 方法装饰器 用的是最多的 function get(params:any){ return function(target:any, methodName:any, desc:any){ log(target); // 类属性 log(methodName); // 方法名字 getData log(desc); // 方法的描述,desc.value是方法描述 target.apiUrl = "xxx"; // 修改雷属性 target.run=function(){ log("run"); } } } class HttpClient{ public url:any | undefined; constructor(){ } @get("https://www.baidu.com") getData(){ log(this.url); } } var http:any = new HttpClient(); log(http.apiUrl); // https://www.baidu.com‘ http.run(); // log run 修改当前的方法(主要作用是装饰方法,并把方法的参数给变换类型): // 这个方法装饰其主要作用就是把参数都给格式化成string类型 function get(params:any){ return function(target:any, methodName:any, desc:any){ log(target); // 类属性 log(methodName); // 方法名字 getData log(desc.value); // 方法 // 想修改下方法,装饰一下,让他们的所有参数变成string类型,并且打印出来 var oMethod = desc.value; desc.value = function(...args:any[]){ args = args.map((value) => { return String(value); }) // 利用apply进行对象冒充,对getdata进行修改,如果没有apply就相当于是把getData方法给替换掉了 oMethod.apply(this, args);// this就是指function(...args:any[])这个函数 } } } class HttpClient{ public url:any | undefined; constructor(){ } @get("https://www.baidu.com") getData(...args:any[]){ log(args); log("我是getData方法"); } } var http:any = new HttpClient(); http.get(123,"xxx"); // 就会先打印["123", "xxx"]后打印 我是getData方法 ######## 方法参数装饰器 用的比较少,类装饰器也可以实现这个功能 运行时候当做函数被调用,可以使用参数张诗琪为累的原型增加一些元素数据,传入下列三个参数: 1对于静态成员来说是类的构造函数,对于实例成员是类的原型对象 2方法的名字 3参数在函数参数列表中的索引 function logParams(params:any){ return function(target:any, methodName:any, paramsIndex:any){ log(params);// xxxx log(target); // 原型对象 log(methodName);// getData log(paramsIndex); // 0 } } class HttpClient{ public url:any | undefined; constructor(){ } getData(@logParams("xxxx") uuid:any){ log(uuid); // iii } } var a = new HttpClient(); a.getData("iii"); 先后输出: 1. xxxx 2. 原型对象 3. getData 4. 0 5. iii ###### 装饰器执行顺序 当存在多个装饰器时候: 执行优先级:属性装饰器>方法装饰器>方法参数装饰器>类装饰器 如果有多个同样的装饰器,它会先从后边执行 #### 其他参考资料 解释原型链,原型对象:[点击这里](https://www.cnblogs.com/chuanq/p/11016509.html) 解释call()、apply():[点击这里](https://www.cnblogs.com/moqiutao/p/7371988.html) 解释prototype:[点击这里](https://www.cnblogs.com/wulihong/p/8906231.html)