TypeScript之泛型, 模块化, 命名空间, 装饰器(TypeScript完结)

TypeScript

TypeScript中的泛型

  1. 泛型的定义 2) 泛型函数 3) 泛型类 4) 泛型接口
泛型:软件工程中,我们不仅要创建一致的定义良好的API,同时也要考虑可重用性。 
组件不仅能够支持当前的数据类型,同时也能支持未来的数据类型,
这在创建大型系统时为你提供了十分灵活的功能。

在像C#和Java这样的语言中,可以使用泛型来创建可重用的组件,
一个组件可以支持多种类型的数据。 这样用户就可以以自己的数据类型来使用组件。

通俗理解:泛型就是解决 类 接口 方法的复用性、以及对不特定数据类型的支持(类型校验)
泛型的定义 和 泛型函数
1.泛型的定义 和 泛型函数  要求传入什么类型的值, 就返回什么类型的值
// 只能返回string类型的数据
function getData(value:string):string{
    return value;
}

// 同时返回 string类型 和number类型 (代码冗余)
function getData1(value:string):string{
    return value;
}
function getData2(value:number):number{
    return value;
}


// 同时返回 string类型 和 number类型  any可以解决这个问题
function getData(value:any):any{
    return '哈哈哈';
}
getData(123);
getData('str');

// 但是any放弃了类型检查 传入number 类型必须返回number类型  
// 传入string类型 必须返回string类型
// 传入的参数类型和返回的参数类型可以不一致
function getData(value:any):any{
    return '哈哈哈';
}

// 泛型:可以支持不特定的数据类型   要求:传入的参数和返回的参数一致
// T表示泛型,具体什么类型是调用这个方法的时候决定的
function getData<T>(value:T):T{
    return value;
}
getData<number>(123);
getData<string>('1214231');
getData<number>('2112');    // 错误的写法  

function getData<T>(value:T):any{
    return '2145214214';
}
getData<number>(123);  // 参数必须是number
getData<string>('这是一个泛型');
泛型类
2.泛型类
泛型类:比如有个最小堆算法,需要同时支持返回数字和字符串a-z两种类型。
通过类的泛型来实现不用泛型写
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(22);
m.add(23);
m.add(6);
m.add(7);
alert(m.min());


// 类的泛型, 用泛型写
class MinClas<T>{
    public list:T[]=[];
    add(value:T):void{
        this.list.push(value);
    }
    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 MinClas<number>(); // 实例化类 并且制定了类的T代表的类型是number
m1.add(11);
m1.add(3);
m1.add(2);
alert(m1.min())


var m2=new MinClas<string>(); //实例化类 并且制定了类的T代表的类型是string
m2.add('c');
m2.add('a');
m2.add('v');
alert(m2.min())
泛型接口
3.泛型接口
// 函数类型接口写法
interface ConfigFn{
    (value1:string,value2:string):string;
}
var setData:ConfigFn=function(value1:string,value2:string):string{
    return value1+value2;
}
setData('name','张三');


// 1.泛型接口:写法一
interface ConfigFn{
    <T>(value:T):T;
}
var getData:ConfigFn=function<T>(value:T):T{
    return value;
}
getData<string>('张三');
// getData<string>(1243);  // 错误


// 2.泛型接口:写法二
interface ConfigFn<T>{
    (value:T):T;
}
function getData<T>(value:T):T{
    return value;
}
var myGetData:ConfigFn<string>=getData;     
myGetData('20');  // 正确
// myGetData(20)  // 错误

/*
        定义一个User的类这个类的作用就是映射数据库字段  
        然后定义一个 MysqlDb的类这个类用于操作数据库   
        然后把User类作为参数传入到MysqlDb中
        var user=new User({
            username:'张三',
            password:'123456'
        })
        var Db=new MysqlDb();
        Db.add(user);
    */

// 把类作为参数来约束数据传入的类型 
class User{
    username:string | undefined;
    pasword:string | undefined;
    }
    class MysqlDb{
        add(user:User):boolean{
            console.log(user);
            return true;
        }
    }
var u=new User();
u.username='张三';
u.pasword='123456';
var Db=new MysqlDb();
Db.add(u);

class ArticleCate{
    title:string | undefined;
    desc:string | undefined;
    status:number | undefined
    }
    class MysqlDb{
        add(info:ArticleCate):boolean{
            console.log(info);
            console.log(info.title);
            return true;
        }
}
var a=new ArticleCate();
a.title="国内";
a.desc="国内新闻";
a.status=1;
var Db=new MysqlDb();
Db.add(a);


// 问题:代码重复, 定义操作数据库的泛型类
class MysqlDb<T>{
    add(info:T):boolean{
        console.log(info);       
        return true;
    }
    updated(info:T,id:number):boolean {
        console.log(info);  
        console.log(id); 
        return true;
    }
}

// 想给User表增加数据
// 1.定义一个User类 和数据库进行映射
class User{
    username:string | undefined;
	pasword:string | undefined;
}
var u=new User();
u.username='张三';
u.pasword='123456';
var Db=new MysqlDb<User>();
Db.add(u);

// 2.相关ArticleCate增加数据  定义一个ArticleCate类 和 数据库进行映射
class ArticleCate{
    title:string | undefined;
    desc:string | undefined;
    status:number | undefined;
    constructor(params:{
                title:string | undefined,
                desc:string | undefined,
                status?:number | undefined
                }){
        this.title=params.title;
        this.desc=params.desc;
        this.status=params.status;
    }
}
// 增加操作
var a=new ArticleCate({
    title:'分类',
    desc:'1111',
    status:1
});
// 类当做参数的泛型类
var Db=new MysqlDb<ArticleCate>();
Db.add(a);

// 修改数据
var a=new ArticleCate({
    title:'分类111',
    desc:'2222'      
});
a.status=0;
var Db=new MysqlDb<ArticleCate>();
Db.updated(a,12);

TypeScript中的模块化

模块的的概念(官方):
    关于术语的一点说明: 请务必注意一点,TypeScript 1.5里术语名已经发生了变化。 “内部模块”现在称做“命名空间”。
    “外部模块”现在则简称为“模块” 模块在其自身的作用域里执行,而不是在全局作用域里;
    这意味着定义在一个模块里的变量,函数,类等等在模块外部是不可见的,除非你明确地使用export形式之一导出它们。 
    相反,如果想使用其它模块导出的变量,函数,类,接口等的时候,你必须要导入它们,可以使用 import形式之一。

模块的概念(自己理解):
    我们可以把一些公共的功能单独抽离成一个文件作为一个模块。
    模块里面的变量 函数 类等默认是私有的,如果我们要在外部访问模块里面的数据(变量、函数、类),
    我们需要通过export暴露模块里面的数据(变量、函数、类...)。
    暴露后我们通过 import 引入模块就可以使用模块里面暴露的数据(变量、函数、类...)。
在db.ts文件中写方法和属性并向外暴露, 在index.ts文件中引入暴露的方法和属性, 并且使用它
db.ts文件
var dbUrl='xxxxxx';

function getData():any[]{
    console.log('获取数据库的数据111');
    return [
        {
            title:'121312'
        },
        {
            title:'121312'
        }
    ]
}

function save(){
    console.log('保存数据成功');
}

export {getData, save, dbUrl};
index.ts文件
/*
    使用 export 向外暴露
    export {getData, save} 向外暴露多个
    export default    默认导出

    每个模块都可以有一个default导出。 
    默认导出使用 default关键字标记;并且一个模块只能够有一个default导出。 
    需要使用一种特殊的导入形式来导入 default导出。

    查看时需要在npm中执行 node index.js 的命令来查看
*/
import { getData, save, dbUrl} from './modules/db'; // 引入 

console.log(dbUrl); // 打印数据

getData(); // 使用
save(); // 使用
注意: 模块化查看时需要在npm中执行 node index.js 的命令来查看

在这里插入图片描述

TypeScript中的命名空间

命名空间:
    在代码量较大的情况下,为了避免各种变量命名相冲突,可将相似功能的函数、
    类、接口等放置到命名空间内
    同Java的包、.Net的命名空间一样,TypeScript的命名空间可以将代码包裹起来,
    只对外暴露需要在外部访问的对象。
    命名空间内的对象通过export关键字对外暴露。

命名空间和模块的区别:
    命名空间:内部模块,主要用于组织代码,避免命名冲突。
    模    块:ts的外部模块的简称,侧重代码的复用,一个模块里可能会有多个命名空间。
namespace A{
    interface Animal {
        name: string;
        eat(): void;
    }
    export class Dog implements Animal {
        name: string;
        constructor(theName: string) {
            this.name = theName;
        }
        eat() {
            console.log(`${this.name} 在吃狗粮。`);
        }
    }
    export class Cat implements Animal {
        name: string;
        constructor(theName: string) {
            this.name = theName;
        }
        eat() {
            console.log(`${this.name} 吃猫粮。`);
        }
    }   
}


namespace B{
    interface Animal {
        name: string;
        eat(): void;
    }
    export class Dog implements Animal {
        name: string;
        constructor(theName: string) {
            this.name = theName;
        }
        eat() {
            console.log(`${this.name} 在吃狗粮。`);
        }
    }
    export class Cat implements Animal {
        name: string;
        constructor(theName: string) {
            this.name = theName;
        }

        eat() {
            console.log(`${this.name} 在吃猫粮。`);
        }
    }   
}
var c=new B.Cat('小花');
c.eat();

TypeScript中的装饰器

装饰器:装饰器是一种特殊类型的声明,它能够被附加到类声明,方法,属性或参数上,可以修改类的行为。
通俗的讲装饰器就是一个方法,可以注入到类、方法、属性参数上来扩展类、属性、方法、参数的功能。
常见的装饰器有:类装饰器、属性装饰器、方法装饰器、参数装饰器
装饰器的写法:普通装饰器(无法传参) 、 装饰器工厂(可传参)
装饰器是过去几年中js最大的成就之一,已是Es7的标准特性之一

1)类装饰器 2)属性装饰器 3)方法装饰器 4)参数装饰器

类装饰器
类装饰器:类装饰器在类声明之前被声明(紧靠着类声明)。 
类装饰器应用于类构造函数,可以用来监视,修改或替换类定义。 传入一个参数

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);
    http.run();

2. 类装饰器:装饰器工厂(可传参)
    function logClass(params:string){
        return function(target:any){
            console.log(target);
            console.log(params);
            target.prototype.apiUrl=params;
        }
    }

    @logClass('http://www.itying.com/api')
    class HttpClient{
        constructor(){
        }

        getData(){

        }
    }
    var http:any=new HttpClient();
    console.log(http.apiUrl);
       
/*
    下面是一个重载构造函数的例子。
    类装饰器表达式会在运行时当作函数被调用,类的构造函数作为其唯一的参数。
    如果类装饰器返回一个值,它会使用提供的构造函数来替换类的声明。
*/
    function logClass(target:any){
        console.log(target);
        return class extends target{
            apiUrl:any='我是修改后的数据';
            getData(){
                this.apiUrl=this.apiUrl+'----';
                console.log(this.apiUrl);
            }
        }
    }

    @logClass
    class HttpClient{
        public apiUrl:string | undefined;
        constructor(){
            this.apiUrl='我是构造函数里面的apiUrl';
        }
        getData(){
            console.log(this.apiUrl);
        }
    }
    var http=new HttpClient();
    http.getData();
属性装饰器
属性装饰器
属性装饰器表达式会在运行时当作函数被调用,传入下列2个参数:
    1、对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
    2、成员的名字。
    类装饰器
    function logClass(params:string){
        return function(target:any){
            console.log(target);
            console.log(params);       
        }
    }

    //属性装饰器
    function logProperty(params:any){
        return function(target:any,attr:any){
            console.log(target);
            console.log(attr);
            target[attr]=params;
        }
    }
    @logClass('xxxx')
    class HttpClient{
        @logProperty('http://itying.com')
        public url:any |undefined;
        constructor(){
        }
        getData(){
            console.log(this.url);
        }
    }
    var http=new HttpClient();
    http.getData();
方法装饰器
方法装饰器
	它会被应用到方法的 属性描述符上,可以用来监视,修改或者替换方法定义。
        方法装饰会在运行时传入下列3个参数:
        1、对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
        2、成员的名字。
        3、成员的属性描述符。
        
    // 方法装饰器一
    function get(params:any){
        return function(target:any,methodName:any,desc:any){
            console.log(target);
            console.log(methodName);
            console.log(desc);
            target.apiUrl='xxxx';
            target.run=function(){
                console.log('run');
            }
        }
    }
    class HttpClient{  
        public url:any |undefined;
        constructor(){
        }
        @get('http://www.itying,com')
        getData(){
            console.log(this.url);
        }
    }
    var http:any=new HttpClient();
    console.log(http.apiUrl);
    http.run();


    // 方法装饰器二
    function get(params:any){
        return function(target:any,methodName:any,desc:any){
            console.log(target);
            console.log(methodName);
            console.log(desc.value);       
            // 修改装饰器的方法  把装饰器方法里面传入的所有参数改为string类型
            // 保存当前的方法
            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('http://www.itying,com')
        getData(...args:any[]){
            console.log(args);
            console.log('我是getData里面的方法');
        }
    }
    var http=new HttpClient();
    http.getData(123,'xxx');
参数装饰器
/*
	4.方法参数装饰器 
    参数装饰器表达式会在运行时当作函数被调用,可以使用参数装饰器为类的原型增加一些元素数据 ,传入下列3个参数:
        1、对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
        2、方法的名字。
        3、参数在函数参数列表中的索引。
*/
    function logParams(params:any){
        return function(target:any,methodName:any,paramsIndex:any){
            console.log(params);
            console.log(target);
            console.log(methodName);
            console.log(paramsIndex);
            target.apiUrl=params;
        }   
    }

    class HttpClient{  
        public url:any |undefined;
        constructor(){
        }           
        getData(@logParams('xxxxx') uuid:any){               
            console.log(uuid);
        }
    }
    var http:any = new HttpClient();
    http.getData(123456);
    console.log( http.apiUrl);
装饰器执行顺序
// 5.装饰器执行顺序
// 属性 -> 方法 -> 方法参数 -> 类  从先到后
// 如果有多个同样的装饰器,它会先执行后面的
    function logClass1(params:string){
        return function(target:any){
        console.log('类装饰器1')
        }
    }

    function logClass2(params:string){
        return function(target:any){
        console.log('类装饰器2')
        }
    }

    function logAttribute1(params?:string){
        return function(target:any,attrName:any){
        console.log('属性装饰器1')
        }
    }

    function logAttribute2(params?:string){
        return function(target:any,attrName:any){
        console.log('属性装饰器2')
        }
    }

    function logMethod1(params?:string){
        return function(target:any,attrName:any,desc:any){
        console.log('方法装饰器1')
        }
    }
    function logMethod2(params?:string){
        return function(target:any,attrName:any,desc:any){
        console.log('方法装饰器2')
        }
    }

    function logParams1(params?:string){
        return function(target:any,attrName:any,desc:any){
        console.log('方法参数装饰器1')
        }
    }

    function logParams2(params?:string){
        return function(target:any,attrName:any,desc:any){
        console.log('方法参数装饰器2')
        }
    }

    @logClass1('http://www.itying.com/api')
    @logClass2('xxxx')
    class HttpClient{
        @logAttribute1()
        @logAttribute2()
        public apiUrl:string | undefined;
        constructor(){
        }
        @logMethod1()
        @logMethod2()
        getData(){
            return true;
        }
        setData(@logParams1() attr1:any,@logParams2() attr2:any,){
        }
    }
    var http:any=new HttpClient();

在这里插入图片描述

注意

1.TypeScript中的泛型有: 泛型的定义、泛型函数、泛型类、泛型接口

2.TypeScript中常见的装饰器有:类装饰器、属性装饰器、方法装饰器、参数装饰器

3.模块化向外暴露 使用:export(向外暴露), export {getData, save}(向外暴露多个), export default(默认导出), 一个模块只能够有一个default导出。

4.块化查看时需要在npm中执行 node index.js 的命令来查看

5.命名空间和模块的区别:
    命名空间:内部模块,主要用于组织代码,避免命名冲突。
    模 块:ts的外部模块的简称,侧重代码的复用,一个模块里可能会有多个命名空间

总结

本篇文章主要介绍了 TypeScript的泛型, 模块化, 命名空间, 装饰器, 本篇文章只是作者给自己写的笔记, 如有任何疑问请移步官网

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值