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. xxxx2. 原型对象3. getData4. 0 5. iii
装饰器执行顺序
当存在 多个装饰器时候:
掘金:前端 LeBron
知乎:前端 LeBron
持续分享技术博文,关注微信公众号 👇🏻