Detailed tutorial typescript (b)
Previous: typescript tutorials explain (a)
Seven, typescript generics
/* 1. 泛型的定义 */
/*泛型,软件工程中,我们不仅要创建一致的定义良好的API,同时也要考虑可重用性。组件不仅能够支持当前的数据类型
,同时也能支持未来的数据类型,这创建大型习时为你提供了十分灵活的功能。
在像C#和Java这样的语言中,可以使用泛型来创建可重用的组件,一个组件可以支持多种类型的数据,这样用户就可以以自己
的数据类型来使用组件。
通俗理解,泛型就是解决 类 接口 方法的复用性、以及对不特定数据类型的支持。
*/
/* 2. 泛型函数 */
//常规的只能返回string类型的数据
function getData1(value:string):string {
return value;
}
// 泛型,可以支持不特定的数据类型 要求,传人的参数和返回的参数一致
// T表示泛型,具体什么类型是调用这个方法的时候决定的
// 第一种写法:
function getData2<T>(value:T):T{
return value
}
// 第二种写法:
// function getData2<T>(value:T):any{
// return value
// }
getData2<number>(123);//参数必须是number
/* 3.泛型类: 比如有个最小堆算法,需要同时支持返回数字和字符串2种类型,通过类的泛型来实现*/
//这样是正常只能传人number
/* class MinClass{
public list:number[]=[];
add(num:number):void{
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
}
} */
// 类的泛型
class MinClass<T>{
public list:T[]=[];
add(num:T[]):void{
this.list = 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 m = new MinClass<number>() /* 实例化类,并定义类型 */
m.add([1,2,3])
console.log(m.min())
/* 把类作为参数来约束数据传人的类型 */
/* class User{
userName:string|undefined;
pasword:string| undefined;
}
class MysqlDb{
add(user:User):boolean{
return true
}
}
var us = new User()
us.userName = "张三"
us.pasword = "123456"
var db = new MysqlDb()
db.add(us) */
// 泛型实现
class MysqlDb<T>{
add(user:T):boolean{
return true
}
}
class User{
userName:string|undefined;
pasword:string| undefined;
}
var us = new User()
us.userName = 'zhangsan'
us.pasword = '1234'
var Db = new MysqlDb<User>()
Db.add(us)
class Artcle {
title:string|undefined;
desc:string|undefined;
constructor(params:{
title:string|undefined,
desc:string|undefined
}){
this.title = params.title
this.desc = params.desc
}
}
var art = new Artcle({
title:'分类',
desc:'111'
})
var Dbs = new MysqlDb<Artcle>()
Dbs.add(art)
// 泛型接口
// 第一种写法:
interface TconfigFn{
<T>(value:T):T;
}
var getData:TconfigFn = function<T>(value:T):T{
return value
}
getData<number>(123)
// 第二种写法
interface TconfigFn2<T>{
(value:T):T;
}
function getData4<T>(value:T):T{
return value
}
var myGetData:TconfigFn2<string> = getData4;
myGetData('20')
Eight, typescript decorators
/* 装饰器:装饰器是一种特殊的类型的声明,它能够被附加到类声明,方法,属性或参数上,可以修改类的行为。
通俗的讲装饰器就是一个方法,可以注入到类,方法、属性参数上类扩展类、属性、方法、参数的功能。
常见的装饰器有:类装饰器、属性装饰器、方法装饰器、参数装饰器
装饰器的写法:普通装饰器(无法传参)、装饰器工厂(可传参)
装饰器是过去几年中js最多的成就之一,已是es7的标准特性之一
*/
//1. 类装饰器:类装饰器在类声明之前被声明(紧靠着类声明)。类装饰器应用于类构造函数,可以用来监视,修改或替换类定义。
// 传人一个参数
// 装饰器(普通装饰器)
function logClass(params:any){
console.log(params)
// params 就是当前类
params.prototype.apiUrl = '动态扩展的属性',
params.prototype.run = function(){
console.log(run)
}
}
@logClass
class HttpClient{
constructor(){
}
getData(){
}
}
var http :any = new HttpClient();
console.log(http.apiUrl)
// 装饰器工厂
function logClass1(params:string){
return function(target:any){
//装饰的类
console.log(target)
target.prototype.apiUrl = params
// 传人的参数
console.log(params)
}
}
@logClass1('传入参数')
class HttpClient1{
constructor(){
}
getData(){
}
}
var http1 :any = new HttpClient1();
console.log(http1.apiUrl)
/*
类装饰器 重载构造函数
重载需要全部重写,否则会报错
类装饰表达式会在运行时当作函数被调用,类的构造函数作为其唯一的参数。
如果类装饰器返回一个值,它会使用提供的构造函数来替换类的声明。
*/
function logClass2(params:any){
return class extends params{
apiUrl:any='我是修改后的数据';
getData(){
this.apiUrl = this.apiUrl+'---'
console.log(this.apiUrl)
}
}
}
@logClass2
class HttpClient2{
public apiUrl:string | undefined
constructor(){
this.apiUrl = '我是构造函数里面的apiUrl'
}
getData(){
console.log(this.apiUrl)
}
}
var http2 :any = new HttpClient2();
console.log(http1.apiUrl)
/*2. 属性装饰器
属性装饰器表达式会在运行时当做函数被调用,传人下列2个参数;
1.对于静态成员来说是类的构造函数,对于实例成员是类的原型对象
2成员名字。
*/
function logPropty(params:any){
return function(target:any,attr:any){
console.log(target,'类的原型对象')
console.log(attr,'成员属性名')//apiUrl
target[attr]=params
}
}
class HttpClient3{
@logPropty('http//:www/baidu.com')
public apiUrl:string | undefined;
constructor(){
}
getData(){
console.log(this.apiUrl)
}
}
var http3 :any = new HttpClient3();
http3.getData()
/* 3.方法装饰器
它会被应用到方法的属性描述符上,可以用来监视,修改或者替换方法定义。
方法装饰会在运行时传入下列3个参数:
1.对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
2.成员的名字
3成员的属性描述符
*/
function logMethod(params:any){
return function(target:any,methodName:any,desc:any){
//修改装饰器的方法 把装饰器方法里面传入的所有参数改为string类型
//1. 保存当前的方法
var oMethod = desc.value;
desc.value = function(...args:any[]){
args = args.map((value)=>{
return String(value);
})
oMethod.apply(this.args);
console.log(args)
}
}
}
class HttpClient4{
public apiUrl:string | undefined;
constructor(){
}
@logMethod('http//:www/baidu.com')
getData(...args:any[]){
console.log('我是内部方法')
}
}
var http4 :any = new HttpClient4();
http4.getData(123,'xxxx')
/* 方法参数装饰器(基本不适用)
参数装饰器表达式会在运行时当做函数被调用,可以使用参数装饰器为类的原型增加一些元素数据,传入下列3个参数:
1. 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象
2. 方法的名字
3. 参数在函数参数列表中的索引
*/
function logParams(params:any){
return function (target:any,methodName:any,index:any){
target.apiUrl = params
}
}
class HttpClient5{
constructor(){
}
getData(@logParams('http//:www/baidu.com')params:any){
console.log(params)
}
}
var http5 :any = new HttpClient5();
http5.getData(11111)
console.log(http5.apiUrl)
// 装饰器执行顺序 先执行class内部,再执行外部类装饰器
/* 属性装饰器1
/* 属性装饰器2
方法参数装饰器1(从右到左)
方法参数装饰器2(从右到左)
方法装饰器1
方法装饰器2
类装饰器2(从下到上)
类装饰器1
*/
function A (params:any){
return function (target:any){
console.log ('类装饰器1')
}
}
function B (params:any){
return function (target:any){
console.log ('类装饰器2')
}
}
function C1(params:any){
return function (target:any,attr:any){
console.log ('属性装饰器1')
}
}
function C2 (params:any){
return function (target:any,attr:any){
console.log ('属性装饰器2')
}
}
function D (params:any){
return function (target:any,methodName:any,desc:any){
console.log ('方法装饰器1')
}
}
function E (params:any){
return function (target:any,methodName:any,desc:any){
console.log ('方法装饰器2')
}
}
function F (params?:any){
return function (target:any,methodName:any,index:any){
console.log ('方法参数装饰器1')
}
}
function G (params?:any){
return function (target:any,methodName:any,index:any){
console.log ('方法参数装饰器2')
}
}
@A('a')
@B('b')
class HttpClient6{
@C1('C1')
public params1: any
@C2('c2')
public params2: any
constructor(){
}
@D('d')
getData(@F()params:any,@G()params2:any){
}
@E('e')
setData(){
}
}
var http6 = new HttpClient6()
Nine, typescript modules and namespaces
/* 模块的概念
我们可以把一些公共的功能单独抽离成一个文件作为一个模块。
模块里面的变量 函数 类等默认是私有的,如果我们要在外部访问模块里面的数据(变量,函数,类),
我们需要通过export暴露模块里面的数据(变量、函数、类...)
暴露后我们通过import引入模块就可以使用模块里面暴露的数据(变量,函数,类)
*/
/* 命名空间概念
命名空间:内部模块,主要用于组织代码,避免命名冲突。
模块:ts的外部模块简称,侧重代码的复用,一个模块里可能会员多个命名空间
*/
/* 命名空间默认为私有,需要在外部使用需要export暴露 */
/* 命名空间模块化,可以通过正常的export和import引入
也可以 ///<reference path ="modle.ts"/>
这样就不需要export暴露命名空间(老版本方式)
*/
namespace A{
interface Anima{
name: string;
eat(str:string):void;
}
export class Pig implements Anima{
name:string;
constructor(name:string){
this.name = name;
}
eat(str:string){
console.log(this.name+str)
}
}
}
namespace B{
interface Anima{
name: string;
eat(str:string):void;
}
export class Pig implements Anima{
name:string;
constructor(name:string){
this.name = name;
}
eat(str:string){
console.log(this.name+str)
}
}
}
var pig = new A.Pig('小猪')
pig.eat('粮食')
块就可以使用模块里面暴露的数据(变量,函数,类)
*/
/* 命名空间概念
命名空间:内部模块,主要用于组织代码,避免命名冲突。
模块:ts的外部模块简称,侧重代码的复用,一个模块里可能会员多个命名空间
*/
/* 命名空间默认为私有,需要在外部使用需要export暴露 */
/* 命名空间模块化,可以通过正常的export和import引入
也可以 ///<reference path ="modle.ts"/>
这样就不需要export暴露命名空间(老版本方式)
*/
namespace A{
interface Anima{
name: string;
eat(str:string):void;
}
export class Pig implements Anima{
name:string;
constructor(name:string){
this.name = name;
}
eat(str:string){
console.log(this.name+str)
}
}
}
namespace B{
interface Anima{
name: string;
eat(str:string):void;
}
export class Pig implements Anima{
name:string;
constructor(name:string){
this.name = name;
}
eat(str:string){
console.log(this.name+str)
}
}
}
var pig = new A.Pig('小猪')
pig.eat('粮食')