TypeScript学习笔记
TS简介
Ts微软开发,包含ES6、包含ES5
编译 tsc xx.ts
每一次都得编译,可以自动编译
开发工具中配置typescirpt自动编译
vscode:
- 创建tsconfig.json文件 tsc --init 生成配置文件
- tsconfig.json配置文件中,修改outDir配置项,取消注释然后修改为.js
- vscode中,点击上方栏位run task,选择ts监听
- 完成
TS类型
var flag = true; flag = 234;
typescript var flag:boolean=true; flag = 131;
|
TS类型:
- boolean
- number
- string
- array数组:
方式1:var arr:number[] = [1,2,3]//制定arr里面全是数字
方式2:var arr:Array= [1,2,3]
元组类型(tuple)
- 方式1:属于数组的一种,即数组中每一个元素指定类型
- 方式2:var arr:[number, string]=[123,“this is ts”];
枚举类型(enum)
enum Flag{ success=1, error=2 } let f:Flag=Flag.error; console.log(f);
enum Color{blue,red,orange}; var c:Color=Color.red; console.log(c); enum Color{blue,red=3,orange}; var c:Color=Color.red; console.log(c); enum Err{ 'undefined'=-1, 'null'=-2, } var c:Err=Err.null console.log(c)
|
任意类型any
var num:any=123; num = true;
|
null类型和undefined其他数据类型的子类型
var num:number | undefined | null;
|
void和java一样 没有返回值类型
function run():void{ console.log('asdf') }
function run():number{ return 1; }
|
never类型,代表从来不会出现的值,是其他类型(包括null‘和undefined)的子类型,代表从不会出现的值
var a:never; a = undefined; a = (()=>{ new Throw Err("报错") })()
|
函数的定义
function run(){ return ... }
var run2 = function(){ return .. }
|
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<rest.length;i++){ sum+=rest[i]; } return sum; }
|
函数重载
- 类似java,同名但是不同参数的多个函数方法
- ts为了兼容es5,以及es6,和java有区别
- es5中,出现同名方法时候,下面的方法会替换上面的方法
- ts中的重载:
function getInfo(name:string):string; function getInfo(age:number):string; function getInfo(str:any):any{ if(typeof str ==="string"){ return "我叫:"+ str; }else{ return "我的年龄是:" + str; } }
|
箭头函数
箭头函数里面的this指向上下文
类
1、ES5中定义类:
function Person(){ this.name='张三'; this.age = 20; } var p = new Person(); alert(p.name);
|
2、构造函数和原型链里面定义
function Person(){ this.name = "张三"; this.age=20; this.run = function()){ alert(this.name+"在运动"); } } Person.prototype.sex="男"; Person.prototype.work=function(){ alert(xx) } var p = new Person(); p.work();
|
3、静态方法
4、es5中的继承-对象冒充继承
function Person(){ this.name = "张三"; this.age=20; this.run = function()){ alert(this.name+"在运动"); } }
Person.prototype.sex="男"; Person.prototype.work=function(){ alert(xx) }
function Web(){ Person.call(this); }
var w = new Web(); w.run(); w.work();
|
function add(c, d) { return this.a + this.b + c + d; }
const obj = { a: 1, b: 2 };
console.error(add.call(obj, 3, 4)); 大统上的说法就是,call改变了this的指向。然后,介绍this xxx什么一大堆名词,反正不管你懂不懂,成功绕晕你就已经ok了,但是实际发生的过程,可以看成下面的样子。
const o = { a: 1, b: 2, add: function(c, d) { return this.a + this.b + c + d } };
给o对象添加一个add属性,这个时候 this 就指向了 o, o.add(5,7)得到的结果和add.call(o, 5, 6)相同。
|
5、原型链继承方法
function Person(){ this.name = "张三"; this.age=20; this.run = function()){ alert(this.name+"在运动"); } }
Person.prototype.sex="男"; Person.prototype.work=function(){ alert(xx) }
function web(){
} web.prototype= new person();
web.work();
|
6、原型链实现继承的问题??无法给父类传参
function Person(){ this.name = "张三"; this.age=20; this.run = function()){ alert(this.name+"在运动"); } }
Person.prototype.sex="男"; Person.prototype.work=function(){ alert(xx) } var p = new person('李四', 20); p.run();
function Web(name,age){
} Web.prototype= new Person(); var w = new Web('sss', 20); w.run();
|
7、原型链+构造函数的组合继承模式
function Person(){ this.name = "张三"; this.age=20; this.run = function()){ alert(this.name+"在运动"); } }
Person.prototype.sex="男"; Person.prototype.work=function(){ alert(xx) } var p = new person('李四', 20); p.run();
function Web(name,age){ Person.call(this,name,age); } Web.prototype = new Person(); var w = new Web('sss', 20); w.run();
|
8、原型链+对象冒充的另一种写法
function Person(){ this.name = "张三"; this.age=20; this.run = function()){ alert(this.name+"在运动"); } }
Person.prototype.sex="男"; Person.prototype.work=function(){ alert(xx) } var p = new person('李四', 20); p.run();
function Web(name,age){ Person.call(this,name,age); } Web.prototype = Person.prototype; var w = new Web('sss', 20); w.run();
|
类的定义
1、ts中定义类,类似java:
class Person(){ name:string; construtor(n:string){ this.name = n; } run():void{ log(this.name); } }
|
2、继承
class Web extends Person{ constructor(name:string){ super(name); } }
var w = new Web("李四"); alert(w.run());
|
3、类里面的修饰符
ts提供了三种修饰符: public(默认的): 公有 在类里面、子类类外边都可以访问 protected:在类里面、子类里面可以访问、在类外部无法访问 private:在类里面可以访问、子类、类外部没法访问
|
静态属性 静态方法
function Person(){ this.run1=function(){
} }
Person.run2=function(){}
调用实例方法:(实例化之后才能调用的) var p = new Person(); p.run1();
调用静态方法: Person.run2();
|
为什么会有静态方法和实例方法之分?
JQuery的实例方法css()方法和静态方法$.get()方法大概原码为:
function Base(element){ this.element = 获取dome节点的方法; this.css = function(str, value){ this.element.style[str] = value; } }
function $(element){ return new Base(element);
}
$.get(){ 。。。 }
实例方法: $("#box").css("color", "red");
静态方法: $.get('url', function(){
)
|
- 另一种方式声明静态方法,利用static关键字声明:
class Person{ public name:string; static sex = "男"; constructor(name:string){ this.name = name; } static print(){ alert("静态方法:"+Person.sex); } }
}
|
多态
父类定义一个方法不去实现,让继承他的子类去实现,每一个子类都有不同的表现
抽象方法
- 用来定义一个标准
- ts中的抽象类,它是提供其他类继承的基类,不能直接被实例化
- 用abstract关键字定义抽象类和抽象方法,抽象类中的抽象方法不包含具体实现并且必须在派生类中实现
- abstract 抽象方法只能放在抽象类中
- 抽象类和抽象方法用来定义标准,基类要求他的子类必须包含某种方法
- 抽象方法只能出现在抽象类中
- 抽象类中必须包含至少一个抽象方法,不然没有意义
abstract class Animal{ abstract eat():any; }
var a = new Animal();
class Dog extends Animal{ eat(){ console.log(this.name + '吃'); } }
var d = new Dog("sss") d.eat();
|
接口
也是定义标准,定义了行为和动作的规范
1、属性接口
function printLabel(labelInfo:{label:string}):void{ console.log(labelInfo.label); } printLabel("ssss");
printLabel({name:"asdf"}); printLabel({label:"sss"});
|
2、接口,对批量方法进行约束
interface FullName{ firstName:string; secondName:string; }
function printName(name:FullName){ log(name.firstName +" "+ name.secondName); }
printName({ age:20, firstName: "张", secondName: "三" })
var obj = { age:20, firstName: "张", secondName: "三" }; printName(obj);
|
3、接口、可选属性,加?问号表示可传可不传
interface FullName{ firstName:string; secondName?:string; }
|
4、模拟封装一个ajax
interface Config{ type:string; url:string; data?:string; dataType:string; }
|
5、函数类型接口、对方法传入的参数、以及返回值进行约束、批量约束
interface encrypt{ (key:string,value:string):string; }
var md5:encrypt=function(key:string, value:string):string{ }
|
6、可索引接口:对数组、对象的约束
var arr:number[]=[123,234]; var arr1:Array<string> = ['123', '222'];
|
interface UserArray{ [index:numer]:string; } var arr:UserArray=['123', '22312'];
|
interface UserObj{ [index:string]:string; } var obj:UserObj={name:"2342"};
|
interface Animal{ name:string; eat(str:string):void; } class Dog implements Animal{ name:string; 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(){ log(this.name+"吃") }
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(){ log(this.name+"吃") }
work(){ log(this.name+"工作") } }
|
泛型
和any有什么区别?
- any放弃了类型检查
- 如果想做到传入什么类型就返回什么类型,例如传入number就返回number,这时候就可以使用泛型
function getData(value:any):any{ return "" }
|
泛型:
- 软件工程中,我们不仅要创建一致的定义好的API,同时也要考虑可重用性,组件不仅能够支持当前的数据类型,同时也能支持未来的数据类型,这在创建大型系统时为你提供了十分灵活的功能
- 在像c#和java中,可以使用泛型来创建可重用的组件,一个组件可支持多种类型的数据,这样用户就可以以自己的数据类型来使用组件
- 通俗理解:泛型就是解决 类 接口 方法的重用性、以及对不特定数据类型的支持
- 可以支持不特定的数据类型
function getData<T>(value:T):T{ return value; }
getData<number>(123123); getData<string>("12131");
function getData<T>(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;i<this.list.length;i++){ if(minNum>this.list[i]){ minNum=this.list[i]; } } return minNum; } }
var m = new MinClass(); m.add(3); m.add(2); log(m.min());
|
- 但是上边的只能传入数字类型,是否可以用泛型解决?可以:
class MinClass<T>{ public list:T[]=[]; add(num:T):void{ this.list.push(num); } min():T{ var minNum = this.list[0]; for(var i = 0;i<this.list.length;i++){ if(minNum>this.list[i]){ minNum=this.list[i]; } } return minNum; } }
var m1 = new MinClass<number>(); m1.add(2); m1.add(4); log(m.min());
|
函数类型接口
interface ConfigFn{ (value1:string, value2:string):string; }
var setData:ConfigFn=function(value1:string, value2:string):string{ return value1 + value2; } setData("name", "张三);
|
interface Config{ <T>(value:T):T; }
var getData:ConfigFn=function<T>(value:T):T{ return value; }
getData<string>("张三");
|
interface Config<T>{ (value:T):T; }
function getData<T>(value:T):T{ return value; }
var myGetData:ConfigFn<string>=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);
|
class MySqlDb<T>{ add(info:T):boolean{ log(info); return true; } } 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);
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<Article>(); Db.add(a);
|
实战:要实现TS封装统一操作Mysql Mongodb Mssql的底层库
interface DBI<T>{ add(info:T):boolean; update(info:T, id:number):boolean; delete(id:number):boolean; get(id:number):any[]; }
class MysqlDb<T> implements DBI<T>{ add(info:T): boolean{ log(info); } update... delete... get... }
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,一个模块只能用一次
暴露:
引入(不用花括号):
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();
var dog = new B.Dog("小花"); dog.eat();
|
装饰器
定义:
- 装饰器是一种特殊类型的声明,他能够被附加到类声明,方法,属性或者参数上,可以修改类的行为。
- 通俗的将装饰器就是一个方法,可以注入到类、方法、属性参数上来扩展类、属性、方法、参数的功能。
- 常见的装饰器有:类装饰器、属性装饰器、方法装饰器、参数装饰器
- 装饰器的写法:普通装饰器(无法传参)、装饰器工厂(可传参)
- 装饰器是过去几年中js最大的成就之一,已经是ES7的标准特性之一
类装饰器:普通装饰器
function logClass(params:any){ console.log(params); params.prototype.apiUrl = "动态扩展的属性"; params.prototype.run=function(){ console.log("我是一个run方法"); } }
@logClass class HttpClient{ constructor(){
} getData(){
} }
|
类装饰器:装饰器工厂
作用:
- 修改构造函数
- 扩展类属性和方法
定义:
function logClass(params:string){ return function(target:any){ 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);
可以修改构造函数的写法 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){ return function(target:any){ log(target); log(params); target.prototype.apiUrl = params; } }
function logProperty(params:any){ return function(target:any, attr:any){ log(target); log(attr); target[attr] = params; } }
@logClass("https://baidu.com") class HttpClient{
@logProperty("http://baidu.com") public url:any | undefined; constructor(){
} getData(){
} }
var http:any = new HttpClient(); console.log(http.apiUrl);
|
方法装饰器
用的是最多的
function get(params:any){ return function(target:any, methodName:any, desc:any){ log(target); log(methodName); log(desc); 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); http.run();
|
- 修改当前的方法(主要作用是装饰方法,并把方法的参数给变换类型):
function get(params:any){ return function(target:any, methodName:any, desc:any){ log(target); log(methodName); log(desc.value);
var oMethod = desc.value; desc.value = function(...args:any[]){ args = args.map((value) => { return String(value); })
oMethod.apply(this, args); } } } 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");
|
方法参数装饰器
用的比较少,类装饰器也可以实现这个功能
- 运行时候当做函数被调用,可以使用参数张诗琪为累的原型增加一些元素数据,传入下列三个参数:
- 1对于静态成员来说是类的构造函数,对于实例成员是类的原型对象
- 2方法的名字
- 3参数在函数参数列表中的索引
function logParams(params:any){ return function(target:any, methodName:any, paramsIndex:any){ log(params); log(target); log(methodName); log(paramsIndex); } } class HttpClient{ public url:any | undefined; constructor(){ } getData(@logParams("xxxx") uuid:any){ log(uuid); } }
var a = new HttpClient(); a.getData("iii");
先后输出: 1. xxxx 2. 原型对象 3. getData 4. 0 5. iii
|
装饰器执行顺序
当存在 多个装饰器时候:
掘金:前端LeBron
知乎:前端LeBron
持续分享技术博文,关注微信公众号👇🏻