TypeScript入门

TypeScript

第一章 概述

1.1 Ts相对于Js的优势

image-20200817084753650.png

1.2 安装解析Ts的工具包

image-20200817090014168.png
image-20200817090324680.png

1.3 第一个Ts代码

1.3.1 步骤

image-20200817091038534.png

1.3.2 简化执行Ts文件的步骤

image-20200817091757316.png

1.4 注释和输出语句

1.4.1 注释

image-20200817092255807.png

1.4.2 输出语句

image-20200817092447005.png

1.5 vscode中如何配置自动编译ts文件

  1. 全局安装 npm i -g typescript

  2. 在需要进行编译的文件夹中,执行命令 tsc --init 创建一个tsconfig.json文件

  3. 修改配置文件中的属性,

    target参数会让TS代码编写成es5的代码
    outDir参数决定了你编译成功的js文件储存在哪里

  4. 点击菜单 终端-运行任务
    在vscode中按 Ctrl + Shift + B

    tsc watch是自动监听,下次保存文件就自动编译
    tsc build是编译一次

第二章 TypeScript中的数据类型

2.1 数值类型

// 数值类型

let a: number = 123;

console.log(a);

// a = 'fdsf'; // 错误 不能将字符类型赋值给number类型

2.2 字符串类型

// 字符类型

let b: string = 'abd';

console.log(b);

2.3 布尔类型

let c: boolean = true;
console.log(c);

2.4 Array类型

// 数组类型

// 方法1
let arr1: string[] = ['1', 'b', '3'];
console.log(arr1);

// 方法2
let arr2: Array = [1, 3, 3];
console.log(arr2);

2.5 元组类型

// 元组类型:是数组类型的一种特例
let arr3: [string, number] = ['1', 2];
console.log(arr3);

2.6 枚举类型

// 枚举类型

enum fruits{
    app,
    banana,
    'xiGua'=3
}

let f: fruits = fruits.xiGua;
console.log(f);

2.7 any类型

// any类型, 任何类型都可以,没有限制

let d: any = '1213';
d = 123;
d = [];
d = {};

2.8 undefined 和 null类型

// undefined和null, 是所有类型的子类型 在非严格模式下可以赋值给别的类型,但是在严格模式下null和undefined只能赋值给void和它们各自
let a1: undefined = undefined;
let a2: null = null;

2.9 void类型

// void空类型 函数没有返回值时使用

function add(a: number, b: number): void {
    a + b;
    return;
}

2.10 never类型

never类型表示的是那些永不存在的值的类型。 例如, never类型是那些总是会抛出异常或根本就不会有返回值的函数表达式或箭头函数表达式的返回值类型; 变量也可能是 never类型,当它们被永不为真的类型保护所约束时。

never类型是任何类型的子类型,也可以赋值给任何类型;然而,没有类型是never的子类型或可以赋值给never类型(除了never本身之外)。 即使 any也不可以赋值给never

// 返回never的函数必须存在无法达到的终点
function error(message: string): never {
    throw new Error(message);
}

// 推断的返回值类型为never
function fail() {
    return error("Something failed");
}

// 返回never的函数必须存在无法达到的终点
function infiniteLoop(): never {
    while (true) {
    }
}

第三章 TypeScript中的函数

3.1 函数的定义

// 定义函数
function add(): string {
    return 'add';
}

let plus = function (a: string, b: number): number {
    return 1124;
}

3.2 可选参数

注意: 可选参数必须配置到参数的最后面

let plus = function (a: string, b: number): number {
    return 1124;
}

plus('12', 232);
// 添加问号可选可不选
let plus1 = function (a: string, b?: number): number {
    return 1124;
}

plus1('12', 232);
plus1('12');

3.3 默认参数

function getInfo(name: string, age: number=16): string {
    return `${name} --- ${age}`;
}

console.log(getInfo('张三')); //张三 --- 16
console.log(getInfo('张三', 20)); // 张三 --- 20

function getInfo1(name: string='李四', age: number=16): string {
    return `${name} --- ${age}`;
}

console.log(getInfo1( '', 20)); //此时必须传递所有参数,否则会报错
console.log(getInfo1('张三子', 40)); // 张三 --- 20

3.4 剩余参数

function getSum(a: number, ...result: number[]): number {
    var sum = a;
    for (let i = 0; i < result.length; i++) {
        sum += result[i];
    }

    return sum;
}

console.log(getSum(1, 2, 3, 4, 5));

3.5 函数重载

java中方法的重载: 重载指的是两个或两个以上同名函数,但是他们的参数不一样,这时会出现函数重载的情况

typescript中的重载,通过为同一函数提供多个函数类型定义来试下多种功能的目的

ts为了兼容es5以及es6, 重载的写法和java中有区别

es5中出现同名方法,下面的会替换上面的方法

// 首先先限制函数的类型, 上面两个声明就是用力定义传入的参数应该是什么样的
function getInfo(name: string): string;
function getInfo(age: number): string;
function getInfo(str: any): any{
    if (typeof str === 'string') {
        return '名字叫做:' + str; 
    } else {
        return '年龄:' + str;
    }
}
// 此时函数只能接受字符串类型的参数或者数值类型的参数,再根据传参结果进行相应的处理

console.log(getInfo('张三'));
console.log(getInfo(30));
// console.log(getInfo(true)) // 传入布尔类型会报错

第四章 TypeScript中的类

4.0 ES5中的类的继承

// 方式1:对象冒充
function Father() {
    this.name = 'father';
    this.age  = '40';
}

function Son() {
    Father.call(this);
}

let son = new Son();
console.log(son); //Son {name: "father", age: "40"},这里看起来像是继承,只不过是改变了构造函数Father中this的指向,自己提娜姬了那些属性
// 对象冒充可以继承构造函数上的属性和方法但是不能继承原型链上的属性和方法
//  有点是实例化字类的时候可以给父类传参


// 方式2 通过原型链继承
Son.prototype = new Father();  

// 原型链继承的问题?
// 实例化子类的时候没有办法给父类传参


// 方式3 原型链 +  对象冒充的组合继承模式(两者优点强强联合)

funtion Son(name, age) {
    Father.call(this, name, age)
}
Son.prototype = new Father();

// 方式3 原型链 + 对象冒充的另一种方式
funtion Son(name, age) {
    Father.call(this, name, age)
}
Son.prototype = Father.prototype;

4.1 ts中类的定义

class Person {
    name: string; // 属性,前面省略了public关键字
    
    constructor(n: string) {
        // 构造函数, 实例化类的时候触发的方法
        this.name= n;
    }
    
    run(): void {
        alert(this.name);
    }
}

var p = new Person('张三');
p.run();

4.2 ts中实现继承 extends super

class Person {
    name: string; // 属性,前面省略了public关键字
    
    constructor(n: string) {
        // 构造函数, 实例化类的时候触发的方法
        this.name= n;
    }
    
    run(): string {
        return `${this.name}在运动`;
    }
}

var p = new Person('张三');
console.log(p.run()); 

class Web extends Person {
    constructor(name: string){
        super(name);
    }
    run(): string {
        return `${this.name}在运动--子类`;
    }
}

var w = new Web('李四');
console.log(w.run());

4.3 类里面的修饰符

typescript里面定义属性的时候给问哦们提供了三种修饰符

public: 公有,在当前类里面、子类、类外面都可以访问

protected: 保护类型 在当前类里面、子类里面可以访问, 在类外部没法访问

private: 私有 在当前类里面可以访问,子类和类外面都没法访问

属性如果不加修饰符,默认就是public

class Person {
    public name: string; // 公有属性
    
    constructor(n: string) {
        // 构造函数, 实例化类的时候触发的方法
        this.name= n;
    }
    
    run(): string {
        return `${this.name}在运动`;
    }
}

// 类里面访问公有属性(是不是在类里面访问的公有属性?是的)
var p = new Person('张三');
console.log(p.run()); 

class Web extends Person {
    constructor(name: string){
        super(name);
    }
    run(): string {
        return `${this.name}在运动--子类`;
    }
}
// 子类可以访问公有属性(这个我这样理解好,调用公有属性是不是在子类的中?是的)
var w = new Web('李四');
console.log(w.run());


// 类外部访问公有属性(调用公有属性是不是在类外面? 是的)
var p = new Person('哈哈哈');
console.log(p.name);

4.4 静态属性 静态方法

通过static关键字可以定义静态属性或者方法,静态方法没有办法直接调用类里面的属性,但是可以在静态方法中调用静态属性

class Person {
    public name: string; // 公有属性
    
    constructor(n: string) {
        // 构造函数, 实例化类的时候触发的方法
        this.name= n;
    }
    
    run(): string { // 实例方法
        return `${this.name}在运动`;
    }
    
    static print() {
        console.log('print静态方法');
    }
    
    static sex="男"
}

var p = new Person('张三');
console.log(p.run()); 

Person.print();

console.log(Person.sex);

4.5 抽象类 继承多态

多态:父类定义一个方法不去实现,让继承它的子类去实现,每一个子类有不同的表现

多态也是继承的一种表现,多态属于继承

class Animal {
    name: string;
    constructor(name: string) {
        this.name = name;
    }
    eat() { // 具体吃什么,不知道,具体吃什么,让继承它的子类去实现,每个子类有不同而表现
        console.log('吃的方法');
    }
}

class Dog extends Animall {
    constructor(name: string){
        super(name);
    }
    
    eat() {
        return this.name + '吃狗粮';
    }
}

class Cat extends Animall {
    constructor(name: string){
        super(name);
    }
    
    eat() {
        return this.name + '吃猫粮';
    }
}

抽象类:typescirpt中的抽象类,他是提供其他继承的基类,不能直接实例化

用abstract关键字定义抽象类和抽象方法,抽象类中的抽象方法不包含具体实现,并且必须在派生类中实现

abstract抽象方法只能放在抽象类里面,抽象类和抽象方法用来定义标准, 例如Animal这个抽象类要求它的子类必须包含eat方法

abstract class Animal {
    public name: string;
     constructor(name: string){
        this.name = name;
     }
    abstract eat(): any;
 }
 
 class Dog extends Animal {
     // 抽象类的子类必须实现抽象类里面的抽象方法,其他方法可以不实现
     constructor(name: string){
         super(name);
     }
     
     eat() {
         return this.name + '吃狗粮';
     }
 }
 
 var dog = new Dog('黑子');
 console.log(dog.eat());

第五章 TypeScript中的接口

接口的作用: 在面向对象的编程中,接口是一种规范的定义,它定义了行为和动作的规范, 在程序设计里面,接口起到了一种限制和规范的作用,接口定义了某一批类所需要遵循的规范,接口不关心这些类的内部状态数据,也不关心这些类里方法的实现细节,它只规定这批类里面必须提供某些方法,同这些方法就可以满足实际需要。typescript中的接口类似于java,同时还增加了更灵活的接口类型,包括属性、函数、可索引和类等

接口也是定义标准

5.1 属性类接口

对json的约束

function printlabel(labelInfo: {label: string}): void {
    console.log('printLabel');
}

printlabel({label: '张三'});

// 对批量方法传入参数进行约束

// 就是对传入对象的约束 属性接口
interface FullName{
    firstName: string; // 注意以分号结束
    secondName: string;
}
function printName(name: FullName) {
    console.log(name.firstName + '--' + name.secondName);
}

printName({
    firstName: '詹姆斯',
    secondName: '卡梅隆' 
})

// 只要对象中包含接口中限制的属性就可以,但是如果直接传递参数的话,就需要和接口完全一致
var obj = { //传入的参数必须包含firstName和secondName
  
    firstName: '詹姆斯',
    secondName: '卡梅隆',
    age: '60'    
}
printName(obj);



// 接口可选属性
interface FullName{
    firstName: string; // 注意以分号结束
    secondName?: string;
    
}

function printName(name: FullName) {
    console.log(name.firstName + '--' + name.secondName);
}
printName({firstName: '詹姆斯'})

5.2 函数类型接口

对方法传入的参数,以及返回值进行约束, 批量约束

// 加密的函数类型接口
interface encrypt {
    (key: string, value: string): string;
}

var md5: encrypt = function (key: string, value: string): string {
    // 模拟操作
    return key + value;
}

console.log(md5('name', '张三'));

5.3 可索引接口

数组、对象的约束(不常用)


// 可索引接口对数组的约束
interface UserArr {
    [index: number]: string;
}

var arr: UserArr = ['aaa', 'bbb'];
console.log(arr[1]);

// 可索引接口对对象的约束

interface UserObj {
    [index: string]: string;
}

var obj: UserObj = {
    name: '20',
    age: '40'
}

5.4 类类型接口

对类的约束 和抽象类有点类似, 你必须实现我的接口中的属性和方法,在实现的时候对于方法你可以不传递参数,也可以传递,但你必须要实现, 如果传递参数的话,需要和接口中方法提供的参数的类型一致

interface Animal {
    name: string;
    
    eat(str: string): void;
}

class Dog implements Animal {
    name: string;
    constructor(name: string) {
        this.name = name;
    }
    
    eat() {
        console.log(this.name + '吃粮食');
    }
}

var d = new Dog('黑子');
d.eat();

class Cat implements Animal {
    name: string;
    constructor(name: string) {
        this.name = name;
    }
    
    eat(food: string) {
        console.log(this.name + food);
    }
}

var c = new Cat('小花');
c.eat('猫粮');

5.5 接口扩展

接口可以继承接口

interface Animal {
    eat(): void;
}

interface Person extends Animal {
    work(): void;
}

class Web implements Person {
    public name: string;
    constructor(name: string) {
        this.name = name;
    }
    eat() {
        console.log(this.name + '喜欢吃馒头');
    }
    work() {
        console.log(this.name + '敲代码');
    }
}

var w = new Web('小明');
w.eat();
w.work();



class Programmer {
     public name: string;
    constructor(name: string) {
        this.name = name;
    }
    coding(code: string) {
        console.log(this.name + code);
    }
}

// 继承的同时实现接口
class Web extends Programmer implements Person {
    constructor(name: string) {
        super(name);
    }
    eat() {
        console.log(this.name + '喜欢吃馒头');
    }
    work() {
        console.log(this.name + '敲代码');
    }
}

var w = new Web('小李');
w.coding('写ts代码');

 

第六章 TypeScript中的泛型

6.1 泛型的定义

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

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

通俗理解: 泛型就是解决类 接口 方法的复用性,以及对不特定数据类型的支持(类型校验)

6.2 泛型函数

// 要求传入什么,返回什么, 比如传入number必须返回number
// 使用any无法实现
function getData(value: any): any {
    return 'fdsfds';
}
// 泛型,可以支持不特定的数据类型,要求: 传入的参数和返回的参数一致, 同时还支持类型的校验
// T表示泛型: 具体是什么类型是调用这个方法 的时候决定的
// 这里可以是T,也可以是别的字符字符串,但是这里推荐使用T

function getData(value: T): T {
    return value;
}

getData(123);

// 错误写法 getData('123');

6.3 泛型类

比如有个最小堆算法,需要同时支持返回数字和字符串两种类型。通过类的泛型来实现

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(2);
m.add(23);
m.add(8);
console.log(m.min());


// 类的泛型
class MinClass {
    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 MinClass(); //实例化类,并且指定了类的T代表的类型是number
m1.add(11);
m1.add(12);
m1.add(14);
console.log(m1.min());

var m2 = MinClass();
m2.add('a');
m2.add('b');
m2.add('c');
console.log(m2.min());

泛类: 泛型可以帮助我们避免重复的代码以及对不特定的数据类型的支持(类型校验), 下面我们看看把类当作参数时的泛型类

  1. 定义一个类
  2. 把类作为参数来约束传入数据输入的类型
// 定义一个User的类这个类的作用就是映射数据库字段
// 然后定义一个MysqlDb的类这个类用于操作数据库
// 然后把User类作为参数传入到MysqlDb中


// 把类作为参数来约束数据传入的类型
class User {
    username: string | undefined;
    password: string | undefined;
}

class MysqlDb {
    add(user: User): boolean {
        console.log(user);
        return true;
    }
}

var user = new User();
user.username = '张三';
user.password = '123456';

var Db = new MysqlDb();
Db.add(user);



// 改为泛型类
class MysqlDb {
    add(user: T): boolean {
        console.log(user);
        return true;
    }
}

class User {
    username: string | undefined;
    password: string | undefined;
}

var user = new User();
user.username = '张三';
user.password = '123456';

var Db = new MysqlDb();
Db.add(user); 

// 定义一个文章分类的类
class ArticleCate {
    title: string | undefined;
    desc: string | undefined;
    state?: number | undefined;
    constructor(params: { 
        title: string | undefined,
        desc: string | undefined, 
        status?: number | undefined}) {
        this.title = params.desc;
        this.desc = params.desc
    }
}

var article = new ArticleCate({
    title: '分类',
    desc: '1242'
});

var Db1 = new MysqlDb();
Db1.add(article);

6.4 泛型接口

// 函数类型接口
interface ConfigFn {
    (value1: string, value2: string): string;
}

var setData: CongfigFn = function (value1: string, value2: string): string {
    return value1 + value2;
}

setData('name', '张三');


// 泛型接口
// 第一种定义泛型接口的方法
interface CongigFn {
    (value: T): T;
}

var getData: ConfigFn = function (value: T): T {
    return value;
}

getData('张三');

// 第二种定义泛型接口的方法
interface ConfigFn {
    (value: T): T;
}

function getData(value: T): T {
    return value;
}

// var myGetData: ConfigFn = getData;
// console.log(myGetData('20'));
var myGetData: ConfigFn = function (name: T): T {
    return name;
};
console.log(myGetData('20'));

第七章 示例

7.1 TypeScript 类型、接口、类 、泛型 综合使用--TypeScript封装统一操作Mysql Mongodb Mssql的底层类库

/*
    功能: 定义一个操作数据库的库,支持Mysql Myssql MongoDb
    要求: 1: Mysql MsSql MongoDb功能一样,都有 add update delete get方法
    注意: 约束统一的规范、以及代码的重用
    
    解决方案: 需要约束规范所以要定义接口,需要代码重用所以用到泛型
    1. 接口: 在面向对象的编程中,接口是一种规范的定义,它定义了行为和动作的规范
    2. 泛型: 通俗理解: 泛型就是解决类接口调用方法的复用性
*/

interface DBI {
    add(info: T): boolean;
    unpdate(info: T, id: number): boolean;
    delete(id: number): boolean;
    get(id: number): any[];
}

// 定义一个操作mysql数据库的类, 注意要实现泛型接口,这个类也应该是一个泛型类
class MysqlDb implements DBI {
    add(info: T): boolean {
        console.log('Mysql addInfo', info);
        return true;
    }
    unpdate(info: T, id: number): boolean {
        throw new Error("Method not implemented.");
    }
    delete(id: number): boolean {
        throw new Error("Method not implemented.");
    }
    get(id: number): any[] {
        throw new Error("Method not implemented.");
    }
    
}

// 定义一个操作mssql数据库的类
class MsSql implements DBI {
    add(info: T): boolean {
        console.log('MsSql addInfo', info);
        return true;
    }
    unpdate(info: T, id: number): boolean {
        throw new Error("Method not implemented.");
    }
    delete(id: number): boolean {
        throw new Error("Method not implemented.");
    }
    get(id: number): any[] {
        throw new Error("Method not implemented.");
    }
}

class User {
    username: string | undefined;
    password: string | undefined;
}

var user = new User();
user.password = '123456';
user.username = '张三';

var oMysql = new MysqlDb();
oMysql.add(user);


var oMsSql = new MsSql();
oMsSql.add(user);

第八章 TypeScript 模块 和 TypeScript 命名空间

模块的概念(官方):

关于术语的一点说明: 请务必注意一定,TypeScritp 1.5里属于名已经发生了变化。“内部模块”现在称作“命名空间”。

“外部模块”现在则简称为“模块"模块在其自身的作用域里执行,而不是在全局作用域里;

这意味着定义在一个模块里的变量, 函数, 类等等再模块外部是不可兼得,除非你明确地使用export形式之一导出它们

相反, 如果想使用其他模块导出的变量, 函数,类,接口等的时候,你必须要导入它们, 可以使用import形式之一。

模块的概念(自己的理解)

我们可以把一些公共的功能单独抽离成一个文件作为一个模块。

模块里面的变量函数类等模式是私有的,如果我们要在外部访问到模块里面的数据(变量, 函数,类),我们需要通过export暴露模块里面的数据(变量, 函数, 类...), 暴露后我们通过import引入模块就可以使用模块里面暴露的数据(变量, 函数, 类...)

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

命名空间和模块的区别:

命名空间: 内部模块,主要用于组织代码,避免命名冲突。

模块: ts的外部模块的简称,侧重代码的复用,一个模块里面可能会有多个命名空间

namespace A{
    interface Animal {
        name: string;

        eat(str: string): void;
    }

    class Dog implements Animal {
        name: string;
        constructor(name: string) {
            this.name = name;
        }

        eat() {
            console.log(this.name + '吃粮食');
        }
    }   
}
   
var d = new A.Dog('狼狗');
d.eat();
// 当然也可以把命名空间当成一个模块导出和引入
    export namespace  B {
        ...
    }
        
    import {B} from '...'

第九章 TypeScript 装饰器

装饰器:装饰器是一种特殊的类型的声明,它能够被附加到类声明,方法,属性或参数上,可以修改类的行为。

通俗的讲装饰器就是一个方法,可以注入到类,方法,属性参数上来扩展类。属性。方法,参数的功能。

常见的装饰器有: 类装饰器,属性装饰器,方法装饰器,参数装饰器

装饰器的写法: 普通装饰器(无法传参),装饰器工厂(可传参)

装饰器是过去几年中js最大的成就之一,已经是ES7的标准特性之一

9.1 类装饰器

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

// 普通装饰器, 传入的参数就是当前对象
function logClass(params: any) {
    console.log(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();
// 装饰器工厂(可传参)
function logClass(params: string) {
    return function (target: any) {
        // 此时target就是当前的类
        console.log(target);
        console.log(params);
        target.prototype.apiUrl = params;
    }
}

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

9.2 属性装饰器

属性装饰器表达式会在运行时当作函数被调用,传入下列2个参数:

  1. 对于静态属性来说就是类的构造函数,对于实例属性来说时类的原型对象
  2. 属性的名字
// 类装饰器
function logClass(params: string) {
    return function (target: any) {
        // 此时target就是当前的类
        //  console.log(target);
        //  console.log(params);
    }
}
// 属性装饰器
function logProperty(params: any) {
    return function (target: any, attr: any) {
        console.log(attr);
        console.log(target);
        target[attr] = params;
    }
    
}
@logClass('hello')
class HttpClient {
    @logProperty('http://www.baidu.com')
    public url: any | undefined;
    @logProperty('name装饰')
    public name: any | undefined;
    constructor() {
        
    }
    getData() {
        console.log(this.url);
        console.log(this.name);
    }
}
var http = new HttpClient();
http.getData();

9.3 方法装饰器

方法装饰器:它会被应用到方法的属性描述符上,可以用来监视,修改后者替换方法定义

方法装饰会在运行时传入下列3个参数:

  1. 对于静态方法来说时是类的构造函数,对于实例方法来说是类的原型对象
  2. 方法的名字
  3. 方法的属性描述符
// 方法装饰器一
function logMethod(params: any) {
    return function(target: any, methodName: any, desc: any) {
        console.log(target);
        console.log(desc);
        console.log(methodName);
        target.apiUrl = 'xxxx';
        target.run = function() {
            console.log('run');
        }
    }
}
class HttpClient {
    public apiUrl: string | undefined;
    constructor() {
        this.apiUrl = '我是构造函数里面的apiUrl'
    }
    @logMethod('http://www/baidu.com')
    getData() {
        console.log(this.apiUrl);
    }
}

var http: any = new HttpClient();

console.log(http.apiUrl);
http.run();
// 方法装饰器2
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);
            })
            console.log(args);
            console.log(this); // 这里竟然是一个HttpClient的实例,确实是应为是实例调用的那个方法
            oMethod.apply(this, args);
        }
    }
}
class HttpClient {
    public apiUrl: string | undefined;
    constructor() {
        this.apiUrl = '我是构造函数里面的apiUrl'
    }
    @logMethod('http://www/baidu.com')
    getData(...args: any[]) {
        console.log(args);
        console.log(this.apiUrl);
    }
}

var http: any = new HttpClient();

console.log(http.apiUrl);
http.getData(123, 'abc');

9.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);
    }
}

class HttpClient {
    public apiUrl: string | undefined;
    constructor() {
        this.apiUrl = '我是构造函数里面的apiUrl'
    }
    getData(@logParams('uuid') uuid: any) {
        console.log(this.apiUrl);
    }
}

var http = new HttpClient();
http.getDat(123456);

9.5 装饰器的执行顺序

属性 》 方法参数装饰器 》方法装饰器 》 类装饰器

如果有多个同样的装饰器,它会先执行后面的

function logClass1(params: any) {
    console.log('类装饰1');
}

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

function logAttribute(params?: any) {
    return function (target: any, attr: any) {
        console.log('属性装饰器');
    }
}

function logMethod(params: any) {
    return function (target: any, methodName: any, desc: any) {
        console.log(params);
    }
}


function logParams1(params: any) {
    return function (target: any, methodName: any, paramsIndex: any) {
        console.log('方法参数装饰器1');
    }
}

function logParams2(params: any) {
    return function (target: any, methodName: any, paramsIndex: any) {
        console.log('方法参数装饰器2');
    }
}

@logClass1
@logClass2
class HttpClient {
    @logAttribute()
    public apiUrl: string | undefined;
    constructor() {
        
    }
    @logMethod('方法装饰器')
    getData(@logParams1('xxxx') name: string, @logParams2('xxxx') age: string) {
        console.log(name);
    }
}


var http: any = new HttpClient();

你可能感兴趣的:(TypeScript入门)