TypeScript学习笔记

163 阅读8分钟

安装TypeScript

npm install -g typescript
tsc helloworld.ts

在VSCode中生成自动编译ts文件的项目

  1. 创建tsconfig.json文件
tsc --init
  1. 点击菜单 终端-生成任务 点击tsc:监视-tsconfing.json

TypeScript中的数据类型

布尔类型(boolean)
数字类型(number)
字符串类型(string)
数组类型(array)
元组类型(tuple)
枚举类型(enum)
任意类型(any)
null 和 undefined
void类型
never类型

TypeScript的使用

  1. 增加了类型校验(写ts代码必须指定类型)
// 布尔类型
var flag:boolean = true; //正确
flag = 3 //错误

// 数字类型
var num:number = 123;
num = 345;
console.log(num); //正确
num='str'; //错误

// 字符串类型(string)
var str:string = 'this is ts';
str='haha';
str=true; //错误

// 数组类型(array) ts中定义数组有两种方式
// var arr = ['1','2']; //es5定义数组
// 1. 第一种定义数组的方法
var arr:number[] = [11,22,33];
console.log(arr);

// 2. 第二种定义数组的方式
var arr:Array<number>=[11,22,33];
console.log(arr);

// 3. 第三种定义数组的方式
var arr:any[] = [11,22,'33'];
console.log(arr);

// 元组类型(tuple) 属于数组的一种(给每一个数组元素指定类型)
// var arr:Array<number> = [11,22,33];
// console.log(arr);

let arr:[number,string] = [123, 'this is ts'];
console.log(arr);

// 枚举类型(enum)
enum 枚举名 {
    标识符[=整型常数],
    标识符[=整型常数],
    ...
    标识符[=整型常数],
}

enum Flag {success=1, error=2};
let s:Flag=Flag.success;
console.log(s);

enum Color {blue, red, 'orange'};
var c:Color=Color.red;
console.log(c); //1 如果标识符没有赋值 它的值就是下标

// 任意类型(any)
var num:any=123;
num='123';
console.log(num);

* 任意类型的用处(用于赋值Object对象类型的值)
var oBox:any=document.getElementById('box');
oBox.style.color='red';

// null 和 undefined 其它 (never类型) 数据类型的子类型
// var num:number;
// console.log(num) //输出:undefined 报错

// var num:undefined;
// console.log(num); //输出: undefined //正确

* 定义没有赋值就是undefined
var num:number | undefined;

var num:null;
num=123; //错误
num=null;

* 一个元素可能是 number类型 可能是 null 可能是 undefined
// var num:number | null | undefined;
// num=1234;
// console.log(num);

// void 类型
* es5的方法定义
function run() {
    console.log('run');
}
run();

* 表示方法没有返回任何类型(typescript)
function run():void {
    console.log('run');
}
run();

* 指定方法需要返回number类型(错误)
function run():number {
    console.log('run');
}
run()

* 指定方法需要返回number类型(正确)
function run():number {
    return 123;
}
run();

// never类型:是其他类型(包括 null 和 undefined 的子类型,代表从不会出现的值)
var a:never;
a=123; // 错误的写法

a=(()=>{
    throw new Error('错误');
})

函数的定义

1. es5中定义函数的方法
// 函数声明法 
function run() {
    return 'run'
}

// 匿名函数
var run = function() {
    return 'run';
}

2. ts中定义函数的方法
// 函数声明法
function run():string {
    return 'run';
}

* 错误写法
function run():string {
    return 123;
}

// 匿名函数
var run  = function():number {
    return 123;
}

3. ts中定义方法传参
function getInfo(name:string,age:number):string{
    return `${name}---${age}`
}

4. 定义没有返回值的方法
function run():void {
    console.log('run');
}
run();

5. ts方法中的参数类型
* 可选参数(?)---es5里面方法的实参和形参可以不一样,但是ts中必须一样,如果不一样就需要配置(可选参数必须配置到参数最后面)
function getInfo(name:string,age?:number):string{
    if(age){
        return `${name} --- ${age}`;
    }else {
        return `${name} --- 年龄保密`
    }
}

* 默认参数--- es5里面没法设置默认参数,es6和ts中都可以设置默认参数
function getInfo(name:string,age:number=20):string{
    if(age){
        return `${name} --- ${age}`;
    }else {
        return `${name} --- 年龄保密`
    }
}

* 剩余参数 --- 三点运算符(接收新参传过来的值)
function sum(a:number,b:number,...result:number[]):number {
    var sum = 0;
    for(var i=0;i<result.length;i++) {
        sum += result[i];
    }
    return sum;
}

* 函数重载---参数不一样函数会重载(兼容es5以及es6的重载写法)
// es5中出现同名方法,下面的方法会替换上面的方法
/*
function css(config) {}
function css(config,value) {}
*/

// ts中的重载(ts的重载跟java重载不一样-假重载)
function getInfo(name:string):string;
function getInfo(age:number):number;
function getInfo(str:any):any {
    if(typeof str === 'string') {
        return '我叫,' + str;
    }else {
        return '我的年龄是' + str;
    }
}


* 箭头函数
(name:string):number=> {}

ts中类的定义和创建

  • es5里面的类
1. 最简单的类  
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(this.name + "在工作");
}

var p=new Person();
p.run();
p.work();

3. 类里面的静态方法
function Person() {
    this.name='张三';
    this.age=20;
    
    this.run=function() {
        alert(this.name + '在运动')
    }
}
Person.getInfo=function() {
    alert("我是静态方法");
}
Person.getInfo();

4. es5里面的继承
function Person() {
    this.name='张三';
    this.age=20;
    
    this.run=function() {
        alert(this.name + '在运动')
    }
}

Person.prototype.sex="男";
Person.prototype.work=function() {
    alert(this.name + "在工作");
}

// Web类 继承Person类  原型链+对象冒充的组合继承模式
function Web() {
    Person.call(this); /* 对象冒充实现继承 */
}
var w=new Web();
w.run();
w.work(); // 对象冒充可以继承构造函数里面的属性和方法,但是不能继承原型链里面的属性和方法

5. es5里面原型链的继承
function Person() {
    this.name='张三';
    this.age=20;
    
    this.run=function() {
        alert(this.name + '在运动')
    }
}

Person.prototype.sex="男";
Person.prototype.work=function() {
    alert(this.name + "在工作");
}

// Web类 继承Person类 原型链+对象冒充的组合继承模式
function Web() {}
Web.prototype=new Person(); //原型链实现基础
var w=new Web();
//原型链既可以继承构造函数的属性和方法也能继承原型链里的属性和方法。
w.run();
w.work()

6. 原型链实现继承的问题
function Person(name, age) {
    this.name=name;
    this.age=age;
    
    this.run=function() {
        alert(this.name + '在运动')
    }
}

Person.prototype.sex="男";
Person.prototype.work=function() {
    alert(this.name + "在工作");
}

function Web(name,age) {
    
}
Web.prototype=new Person();
var w = new Web('赵四', 20); //实例化子类的时候没法给父类传参
w.run();

7. 原型链+构造函数的组合继承模式
function Person(name, age) {
    this.name=name;
    this.age=age;
    
    this.run=function() {
        alert(this.name + '在运动')
    }
}

Person.prototype.sex="男";
Person.prototype.work=function() {
    alert(this.name + "在工作");
}
function Web(name,age) {
    Person.call(this, name, age); // 对象冒充继承 实例化子类可以给父类传参
}

Web.prototype=new Person();
var w=new Web('赵四', 20);
w.run(); // 可以调用父类的构造函数
w.work(); // 可以调用原型链的方法

8. 原型链+对象冒充的另一种方法
function Person(name, age) {
    this.name=name;
    this.age=age;
    
    this.run=function() {
        alert(this.name + '在运动')
    }
}

Person.prototype.sex="男";
Person.prototype.work=function() {
    alert(this.name + "在工作");
}
function Web(name,age) {
    Person.call(this, name, age); // 对象冒充继承 实例化子类可以给父类传参
}

Web.prototype = Person.prototype;
  • typescript中的类
1. ts中类的定义
class Person {
    name: string; // 属性 前面省略了public关键词
    constructor(n:string) { // 构造函数 实例化类的时候触发的方法
        this.name = n;
    }
    
    getName():string {
        return this.name;
    }
    setName(name:string):void {
        this.name=name;
    }
}
var p =new Person('张三');
p.getName() // 张三
p.setName('李四');

2. ts中实现继承 extends、super
class Person{
    name: string;
    constructor(name: string) {
        this.name = name;
    }
    
    run():string {
        return `${this.name}在运动`
    }
}
var p = new Person();
p.run();

class Web extends Person{
    constructor(name: string) {
        super(name); /* 初始化父类的构造函数 */
    }
    run():string {
        return `${this.name}在运动-子类`
    }
    work() {
        alert(`${this.name}在工作`);
    }
}
var w = new Web("李四");
w.run(); // 执行子类的方法

3. ts类里面的修饰符 typescript里面定义属性的时候给我们提供了 三种修饰符
/*
    public    : 公有   在类里面、子类、类外面都可以访问
    protected : 保护类型   在类里面、子类里面都可以访问,在类外部没法访问
    private   : 私有  在类里面可以访问,子类,类外部都没法访问
    
    属性如果不加修饰符,默认为公有
*/

//public修饰符的演示
class Person{
    public name: string;
    constructor(name: string) {
        this.name = name;
    }
    
    run():string {
        return `${this.name}在运动`
    }
}
var p = new Person();
p.run();

// 类外部访问公有属性
var p = new Person("哈哈哈");
p.name;

// protected修饰符的演示
class Person{
    protected name: string;
    constructor(name: string) {
        this.name = name;
    }
    
    run():string {
        return `${this.name}在运动`
    }
}
var p = new Person();
p.run();

class Web extends Person{
    constructor(name: string) {
        super(name); /* 初始化父类的构造函数 */
    }
    run():string {
        return `${this.name}在运动-子类`
    }
    work() {
        alert(`${this.name}在工作`);
    }
}
var w = new Web("李四");
w.run(); // 执行子类的方法

// private 修饰符的演示-内外部都不能调用(编译报错)
  • typescript中的接口定义
1. 接口的类型
属性类接口
函数类型接口
可索引接口
类类型接口
接口扩展

2. 属性接口-对json的约束
/* ts中自定义方法传入参数对 json 进行约束 */
function printLabel(labelInfo: { label: string }): void {
  console.log('printLabel');
}
printLabel('hahaha'); /* 错误写法 */

printLabel({name: '张三'}); /* 错误的写法 */

printLabel({label: '张三'}) 

/* 用属性接口对传入参数进行约束 */
interface FullName {
    firstName:string;
    secondName:string;
}

function printName(name: FullName) {
    console.log(name.firstName + '--' + name.secondName);
}
var obj = {age:20,firstName:'张',secondName:'三'}; /* 传入的参数必须包含 firstName */
printName(obj);

/* 接口可选属性的属性接口 */
interface FullName {
    firstName?: string;
    secondName?: string;
}
function getName(name: FullName) {
    console.log(name);
}
getName({firstName: 'firstName', secondName: 'secondName'})

/* 接口可选属性例子 */
interface Config{
    type: string;
    url: string;
    data?:string;
    dataType:string;
}

function ajax(config:Config) {
    var xhr = new XMLHttpRequest();
    xhr.open(confing.type,config.url,true);
    xhr.send(config.data);
    xhr.onreadystatechange=function() {
        if(xhr.readyState == 4 && xhr.state == 200) {
            console.log('成功');
            if(config.dataType == 'json') {
                JSON.parse(xhr.responseText);
            }else {
                console.log(xhr.responseText);
            }
        }
    }
}

ajax({
    type: 'get',
    url: 'http://www.baidu.com',
    dataType: 'json'
})

3. 函数类型接口:对方法传入的参数以及返回值进行约束-批量约束
/* 加密的函数类型接口 */
interface encrypt {
    (key:string,value:string):string;
}
var md5:encrypt=function(key:string,value:string):string {
    //模拟操作
    return key+value;
}
md5('name', 'zhangsan1');

var sha1:encrypt=function(key:string,value:string):string {
    return key+value;
}

4. 可索引接口:数组、对象的约束(不常用)
/* 可索引接口对数组的约束 */
interface UserArr {
    [index:number]:string
}
var arr:UserArr=['123','123'];

/* 可索引接口对对象的约束 */
interface UserObj {
    [index:string]:string
}
var arr:UserObj={name: '20', age:20};

5. 类类型接口:对类的约束 和 抽象类有点相似
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 + "吃粮食")
    }
}

6. 接口扩展:接口可以继承接口
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 + "写代码")
    }
}

/* 实现+继承结合的用法 */
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中的泛型,泛型变量,泛型类
1. 泛型的定义和泛型函数
// 泛型: 可以支持不确定的数据类型 要求:传入的参数和返回的参数一致
function getData<T>(value:T):T {
    return value;
}

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

// 固定类型的类
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;
    }
}

// 类的泛型
class MinClass<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 MinClass<number>(); /*实例化类 并且指定了类的代表类型是number*/
var m2=new MinClass<string>(); /*实例化类 并且指定了类的代表类型是string*/

3. 泛型接口
// 函数类型接口
interface ConfigFn{
    (value1:string,value2:string):string;
}
var setData:ConfigFn=function(value1:string,value2:string):string {
    return value1+value2;
}

// 第一种泛型接口
interface ConfigFn {
    <T>(value:T):T;
}
var getData:ConfingFn=function<T>(value:T):T {
    return value;
}
getData<string>('张三');
getData<string>(123) // 错误

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

4. 把类作为参数类型的泛型类
/*
定义一个User的类,这个类的作用就是映射数据库字段,然后定义一个MysqlDb的类这个类的作用用于操作数据库,然后把User类作为参数传入到MysqlDB中
*/

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

class MysqlDB {
    add(user:User):boolean {
        console.log(user)
        return true;
    }
}
var u=new User();
u.username='张三';
u.password='123456';

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

/*给其它表增加数据*/
class ArticleCate {
    title: string | undefined;
    desc:string | undefined;
    status:number | undefined;
}

class MysqlDb {
    add(info:ArticleDate):boolean {
        console.log(info);
        return true;
    }
}

var a = new ArticleCate();
a.title='国内';
a.desc='国内新闻';
a.status=1;
var Db = new MysqlDb();
Db.add(u);

// 操作数据库的泛型类
class MysqlDb<T> {
    add(info:T):boolean {
        console.log(info);
        return true;
    }
    updated(info:T,id:number):boolean {
        consoel.log(info);
        console.log(id);
        return true;
    }
}

class User {
    username: string | undefined;
    password: string | undefined;
}
var u = new User();
u.username='张三';
u.password='123456';
var Db = new MysqlDb<User>();
Db.add(u);

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'
});
a.status=0;
var Db=new MysqlDb<ArticleCate>();
Db.updated(a,12);

5. 定义一个操作数据库的库 支持Mysql Mssql MongoDb
interface DBI<T> {
    add(info:T):boolean;
    update(info:T,id:number):boolean;
    delete(id:number):boolean;
    get(id:number):any[];
}
//定义一个操作mysql数据库的类 注意:要实现泛型接口 这个类也应该是一个泛型类
class MysqlDb<T> implements DBI<T> {
  constructor() {
    console.log('数据库建立连接');
  }
  add(info: T): boolean {
    console.log(info);
    throw new Error("Method not implemented.");
  }
  update(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[] {
    var list = [{}];
  }    
}

// 操作用户表 定义一个User类和数据库表做映射
class User {
    username:string | undefined;
    password:string | undefined;
}
var u = new User();
u.usrname = '张三';
u.password='123456';

var oMysql=new MysqlDb<User>(); // 类作为参数来约束数据传入的类型
oMysql.add(u);
  • typescript中类的静态方法,静态方法,抽象类,多态
1. typescript类的静态方法(static)
class Person {
    public name:string;
    public age:number=20;
    static sex = '男';
    constructor(name:string) {
        this.name=name;
    }
    run() {/* 实例方法 */
        alert(`${this.name}在运动`)
    }
    work() {
        alert(`${this.name}在工作`)
    }
    static print() { /* 静态方法 */
        alert('print方法')
    }
    static printAge() { /* 错误,静态方法中不能直接调用类中的属性 */
        alert(`${this.age}`)
    }
    static printSex() {
        alert(`${Person.sex}`);
    }
}

2. 抽象类和抽象方法
// 用abstract关键字定义抽象类和抽象方法,抽象类中的抽象方法不包含具体实现并且必须在派生类中实现。
// abstract抽象方法只能放在抽象类里面
// 抽象类和抽象方法用来定义标准-标准:Animal这个类要求它的子类必须包含eat方法
abstract class Animal{
    public name:string;
    construtor(name:string) {
        this.name = name;
    }
    abstract eat():any;
    run() {
        console.log('其他方法可以不实现');
    }
}
var a = new Animal(); // 错误的写法
class Dog extends Animal {
    // 抽象类的子类必须实现抽象类里面的抽象方法
    constructor(name:string) {
        super(name)
    }
    eat() {
        console.log(this.name + '吃粮食');
    }
}

3. typescript的多态(多态属于继承)
  • 模块的使用
/*
我们可以把一些公共的功能单独抽离成一个文件作为一个模块。
模块里面的变量 函数 类等默认是私有的,如果我们要在外部访问莫跨里面的数据(变量、函数、类),
我们需要通过export暴露模块里面的数据(变量、函数、类...)。
暴露后我们通过 import 引入模块就可以使用模块里面暴露的数据 (变量、函数、类...)。
*/

export 和 import
/*
// 定义在modules中的class
export class MysqlDb<T> implements DBI<T> {
  constructor() {
    console.log('数据库建立连接');
  }
  add(info: T): boolean {
    console.log(info);
    throw new Error("Method not implemented.");
  }
  update(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[] {
    var list = [{}];
  }    
}

// 导入引用的方法
import {MysqlDb} from './modules/db';
*/
  • typescript的命名空间
export 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} 吃猫粮。`)
        }
    }
}

var d=new A.Dog('狼狗');
d.eat();

/*
import {A} from "./modules/a";
*/
  • typescript的装饰器
/*
装饰器:装饰器是一种特殊类型的声明,他能够被附加到类声明,方法,属性或参数上,可以修改类的行为。
同属的讲装饰器就是一个方法,可以注入到类的方法、属性参数上来扩展类、属性、方法、参数的功能。
常见的装饰器有:类装饰器、属性装饰器、方法装饰器、参数装饰器、
装饰器的写法:普通装饰器(无法传参)、装饰器工厂(可传参)
*/

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

1.1 装饰器:普通装饰器(无法传参)
function logClass(params:any) {
    console.log(parmas);
    // params 就是当前类
    params.prototype.apiUrl='xxx'
    params.prototype.run=function() {
        console.log("我是一个run方法");
    }
}

@logClass
class HttpClient {
    constructor() {
    
    }
    getData() {
        
    }
}

1.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() {
        console.log(this.apiUrl);
    }
}

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

2. 属性装饰器
/*
属性装饰器表达式会在运行时当做函数卑调用,传入下列2各参数:
1. 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
2. 成员的名字
*/
function logProperty(params:any) {
    return function(target:any, attr:any) {
        console.log(target);
        console.log(attr);
        target.attr=params;
    }
}

@logProperty('http://itying.com');
class HttpClient {
    public url:any | undefined;
    constructor() {}
    getData() {}
}

3. 方法装饰器
/*
它会被应用到方法的 属性描述符上,可以用来监控,修改或者替换方法定义。
方法装饰会在运行时传入下列3个参数:
1. 对于静态成员来说是类的构造函数,对于实力成员是类的原型对象
2. 成员的名字
3. 成员的属性描述符
*/
function logMethod(params:any) {
    return function(target:any, methodName:any,desc:any) {
        console.log(target);
        console.log(methodName);
        console.log(desc.value);
        
        //修改装饰器的方法 把装饰器方法里面传入的所有参数改为string类型
        
        //1、保存当前的方法
        
        var oMethod = desc.value;
        desc.value=function(...args:any[]) {
            args=args.map((value) => {
                return String(value);
            })
            
            console.log(args);
            oMethod.apply(this, args);
        }
        
    }
}

class HttpClient {
    public url:any | undefined;
    constructor() {}
    @logMethod('http://www.itying.com')
    getData(...args:any[]) {
        console.log(args);
        console.log(this.url);
    }
}

var http:any=new HttpClient();
console.log(http.apiUrl);
http.run();

4. 方法参数装饰器
/*
参数装饰器表达式会在运行时当做函数被调用,可以使用参数装饰器为类的原型增加一些元素数据,传入下列3个参数:
1. 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
2. 方法的名字。
3. 函数在函数参数列表中的索引。
*/

function logParams(params:any) {
    return function(target:any, methodName: any, paramsIndex: any) {
        console.log(target);
        console.log(paramsName);
        console.log(paramsIndex);
    }
}

class HttpClient {
    public url:any | undefined;
    constructor() {}
    
    getData(@logparams('uuid') uuid:any) {
        console.log(args);
        console.log(this.url);
    }
}
var http = new HttpClient();
http.getData(12356);

5. 装饰器执行顺序
/*
属性>>方法>>方法参数>>类
如果有多个同样的装饰器,它会先执行后面的
*/