TypeScript 学习笔记(一)—— 参考哔哩哔哩整理的笔记

1.TypeScript 介绍 、自动编译

1.1 介绍

  • TypeScript 是由 微软 开发的开源语言
  • TypeScript 是 Javascript 的超集,遵循最新的 ES6、ES5 规范,扩展 JavaScript 语法
  • TypeScript 更像后端 java、C#这样 面向对象 语言,可以让 js 开发大型企业项目
  • 谷歌也支持 Typescript ,它的 Angular2.x+ 就是基于 Typescript 语法
  • 最新的 Vue ,React ,Nodejs 框架 Nestjs、midway 中,用的是 TypeScript 语法

1.2 自动编译

  • 生成 tsconfig.json:tsc --init 
  • 修改输出目录:"outDir": "./js"

终端 - 运行任务 - 监视 tsconfig.json

2.TypeScript 数据类型

在 TypeScript 中,写代码必须指定数据类型,如下所示:

① 布尔类型(boolean);

let flag: boolean = true;

flag = 123; // 错误
flag = false; // 正确

② 数字类型(number);

let num: number = 123;

num = 'str'; // 错误
num = 456; // 正确

③ 字符串类型 (string);

let str: string = 'this is ts';

str = 'haha'; // 正确
str = true; // 错误

④ 数组类型(array);

// 第一种方式
let arr: number[] = [11, 22, 33];

// 第二种方式【不推荐前端这么写,一般后端习惯这么定义,写法类似于泛型】
let arr: Array<number> = [11, 22, 33];

⑤ 元组类型(tuple):数组的一种,指定一个数组中 不同类型 的元素;

let arr: [number, string] = [123, 'this is ts'];

⑥ 枚举类型(enum):处理非数值数据;

// 第一种情况:标识符有赋值
enum Flag {
  'success' = 1,
  'error' = 2,
};
let aaa: Flag = Flag.success; // 1

// 第二种情况:标识符没有赋值
enum Color {
  blue,
  red,
  'orange',
};
let bbb: Color = Color.red;  // 输出1,如果 标识符 没有赋值,它的值就是数组下标

// 第三种情况:有的标识符有赋值,有的标识符没有赋值
enum Color {
  blue,
  red = 3,
  'orange',
};
const ccc: Color = Color.orange;  // 输出4,前面的标识符赋值了,所以后面比前面的 + 1

⑦ 任意类型(any):重新赋值为不同类型的值不会报错,可以用于获取 DOM 元素类型;

let num: any = 123;

// 赋值为不同类型不会报错
num = 'str';

// 获取 DOM 元素时,如果不定义 DOM 类型,可以使用 any 防止报错
let oBox: any = document.getElementById('box');
oBox.style.color = 'red';

⑧ null 、undefined:其他( 除了 never 类型 )数据类型的子类型;

// 一个元素可能是 number 类型 可能是 null 可能是 undefined
// 定义没有赋值就是 undefined
let num: number | null | undefined;

⑨ void: 定义方法时,方法没有返回值,使用 void 作为返回值类型;

// 注意:方法返回值类型 不可以 写 undefined !!!
function run(): void {
  console.log('runrunrunrun');
}

⑩ never:其他类型 (包括 null 和 undefined)的子类型,从不会出现的值,只能被 never 赋值;

let a: never;

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

3.TypeScript 函数、参数、重载

3.1 函数、参数

3.1.1 函数定义

函数声明法、匿名函数法

// 函数声明法 参数指定类型,返回值也要指定类型
function getInfo(name: string, age: number): string {
  // ES6模板字符串语法 返回值要和指定类型一致
  return `${name} --- ${age}`;
}
alert(getInfo('zhangsan', 20));

// 匿名函数法
const getInfo = function (name: string, age: number): string {
  return `${name} --- ${age}`;
}
alert(getInfo('zhangsan', 40));

// 没有返回值 void
function run(): void {
    console.log('runrunrunrun')
}

3.1.2 可选参数 和 默认参数

es5 里实参形参可以不一样,但是 ts 中实参、形参必须一样;如果实参、形参不一样,就需要配置 可选参数,且 可选参数 必须配置到 必选参数 的后面

es5 里没法设置默认参数值,es6 和 ts 中,可以设置默认参数值;

// 可选参数 ? 就代表可选参数,必须写 “最后” !!!
function getInfo(name: string, age?: number): string {
  if (age) {
      return `${name} --- ${age}`;
  } else {
      return `${name} --- 年龄保密`;
  }
}

alert(getInfo('zhangsan'))
alert(getInfo('zhangsan', 123))

// 默认参数(es6、ts)
function getInfo(name: string, age: number = 20): string { ... }

3.1.3 剩余参数

// 剩余参数
// ...result: number[] 含义:将剩余参数存储在 result(可变名字)数字类型数组中

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

alert(sum(1,2,3,4,5,6)) ;

3.2 重载、重写

3.2.1 重载(Java VS TypeScript VS ES5)

Java 中的重载:两个或者两个以上 同名函数,但参数不一样

TypeScript 中的重载:同一个函数,提供多个函数类型定义

ES5 没有重载的说法,若出现同名方法,下面的会替换上面的方法( 即使参数个数不同 )

// TypeScript 中的方法重载
function getInfo(name: string): string;
function getInfo(name: string, age: number): string;
function getInfo(name: any, age?: any): any {
  if (age) {
    return '我叫:' + name + '我的年龄是' + age;
  } else {
    return '我叫:' + name;
  }
}

3.2.2 重写

Java 中的重写:重写是 子类对父类 的 某方法 重写逻辑,返回值和形参都不能变(外壳不变,核心重写)

4.TypeScript 类、继承、多态、抽象类

4.1 类、静态 / 实例属性、静态 / 实例方法、原型链上的属性方法

  • 实例属性 / 实例方法:定义在构造函数里,通过实例化类的对象,使用对象进行调用
  • 静态属性 / 静态方法:定义在类名上,通过类名直接调用

4.1.1 ES5 中的类、静态 / 实例属性、静态 / 实例方法、原型链上的属性方法

// ES5        
function Person() {
  /* 实例属性:构造函数中的属性 */
  this.name = '张三';
  /* 实例方法:构造函数中的方法 */
  this.run = function () {
    alert(this.name + '在运动');
  }
}

// 调用实例方法:先实例化类,然后对象调用
var p = new Person();
p.work();

/* 静态方法:定义在类名上的方法 */
Person.getInfo = function () {
  alert('静态方法:定义在类名上的方法');
}
// 调用静态方法:直接通过类名调用
Person.getInfo();

// 原型链上面的属性会被多个实例共享,构造函数不会
/* 原型链上的属性 */
Person.prototype.sex = "男";
/* 原型链上的方法 */
Person.prototype.work = function () {
  alert(this.name + '在工作');
}

4.1.2 TypeScript 中的类、静态 / 实例属性、静态 / 实例方法

// TypeScript
class Per {
  /* 实例属性 */
  public name: string;
  public age: number = 20;
  /* 静态属性 */
  static sex = "男";
  
  constructor(name: string) {
    this.name = name;
  }
  /* 实例方法 */
  run() {  
    alert(`${this.name}在运动`)
  }
  /* 静态方法 */
  static print() {
    /* 不能直接调用类里面的属性(sex)要写成(Per.sex) */
    alert('print方法' + Per.sex);
  }
}

let p = new Per('张三');
/* 实例方法:通过 实例化类的对象 调用 */
p.run(); 
/* 静态方法:通过 类名 直接调用 */
Per.print();
/*静态属性:通过 类名 直接调用*/
alert(Per.sex);

4.2 对象冒充继承、原型链继承、原型链 + 对象冒充组合继承

  • 原型链上面的属性会被多个实例共享(即创建新的实例后,不需要重新开辟空间存储上面的变量),构造函数不会
  • 原型链继承、对象冒充继承、原型链+对象冒充组合继承:
  1. 对象冒充继承:可以继承*构造函数*里面的属性和方法,不能继承*原型链*上面的属性和方法(实例化子类可以传参)
  2. 原型链继承:可以继承*构造函数*里面的属性和方法,也可以继承*原型链*上面的属性和方法(实例化子类无法传参)
  3. 原型链+对象冒充组合继承:可以继承*构造函数*里面的属性和方法,也可以继承*原型链*上面的属性和方法(实例化子类可以传参)
        // Web类 继承 Person类
        function Web() {
            Person.call(this);    /*对象冒充实现继承*/
        }
        var w = new Web();
        w.run();  // 对象冒充可以继承构造函数里面的属性和方法
        w.work();  // 对象冒充没法继承原型链上面的属性和方法
————————————————————————————————————————————————————————————————————————————
        function Web() {}
        Web.prototype = new Person();  /*原型链实现继承*/
        var w = new Web(); // 可以继承构造函数里面的属性和方法 也可以继承原型链上面的属性和方法
        w.run();
        w.work();
————————————————————————————————————————————————————————————————————————————
    function Web(name,age) {}    /*原型链实现继承 缺点*/
    Web.prototype = new Person();
    var w = new Web('赵四',20);   // 实例化没法*传参*
    w.run();
————————————————————————————————————————————————————————————————————————————
    function Web(name,age){    /*原型链+对象冒充组合继承*/
        Person.call(this,name,age);   // 对象冒充继承   实例化子类可以给父类传参
    }
    Web.prototype = new Person();    // 原型链继承 可以继承原型链上的方法
    Web.prototype = Person.prototype; // 这么写也行
    var w = new Web('赵四',20);

4.3 类的定义和继承(extends / super)

  • TS中实现继承:extends super
  • 构造函数:实例化类的时候触发的方法
        function Person(name){    // ES5 中定义类
            this.name = name;
            this.run = function(){
                console.log(this.name)
            }}
        var p = new Person('张三');
        p.run()
——————————————————————————————————————————————————————————————————————————
    class Person {    // TS 中定义类
        name:string;    // 属性定义 前面省略了 public关键词
        constructor(name:string) {  // 构造函数 实例化类的时候触发的方法
            this.name = name;
        }
        getName():string {
            return `${this.name}在运动`;
        }
        setName(name:string):void{
            this.name = name;
        }}
    var p = new Person('张三');
    p.setName('李四');
    alert(p.getName());
——————————————————————————————————————————————————————————————————————————
    class Web extends Person{    // TS中实现继承 extends super
        constructor(name:string){
            super(name);  /* 初始化父类的构造函数 */
        }}
    var w = new Web('赵六');
    alert(w.getName());

4.4 类修饰符

  •     public:公有 —— 任何类都能访问(不写默认是 public)
  •     protected:保护类型 —— 当前类及子类可访问
  •     private:私有 —— 当前类可访问

4.5 多态(属于继承)

  • 多态:父类定义一个方法不去实现,让继承它的子类去实现  每一个子类有不同的表现
                class Animal { // 父类
                    name:string;
                    constructor(name:string) {
                        this.name = name;
                    }
                    eat(){ // 具体吃什么不知道,具体吃什么?继承它的子类去实现
                        console.log('吃的方法')
                    }}
                class Dog extends Animal{ // 子类1
                    constructor(name:string){
                        super(name)
                    }
                    eat(){ // 子类多态的样子
                        return this.name+'吃粮食'
                    }}
                class Cat extends Animal{ // 子类2
                    constructor(name:string){
                        super(name)
                    }
                    eat(){ // 子类多态的样子
                        return this.name+'吃老鼠'
                    }}

4.6 抽象类 abstract

  • 抽象类中的抽象方法*不包含*具体实现,必须在*派生类*中实现,抽象方法只能放在抽象类里
  • 抽象类和抽象方法用来*定义标准*(标准:Animal 这个类要求它的子类必须包含 eat 方法)
  • 抽象类:提供其他类继承的基类,不能直接被实例化,不能实现抽象方法
  • 抽象类的派生子类:必须实现抽象方法,可以实例化
abstract class Animal{ // 抽象类:定义标准,不能实现抽象方法,不可以实例化
    public name:string;
    constructor(name:string){
        this.name = name;
    }
    abstract eat():any;  // 抽象方法*不包含*具体实现,并且必须在派生类中实现
    run(){
        console.log('其他方法可以不实现')
    }}
// var a = new Animal() /*错误的写法 抽象类不可以实例化!!!!*/

class Dog extends Animal{ // 抽象类的派生类:必须实现抽象方法,可以实例化
    constructor(name:any){
        super(name)
    }
    eat(){ // 抽象类的子类必须实现抽象类里的抽象方法
        console.log(this.name+'吃粮食')
    }}
var d = new Dog('小花花');
d.eat();

5.Typescript 接口

  • 接口的作用:行为和动作的规范,对批量方法进行约束,只规定这批类里必须提供某些方法,不规定方法内容
  • Typescrip 中的接口类似于 Java,还增加了更灵活的接口类型,包括属性、函数、可索引和类

5.1 属性类型接口

  • 属性类接口定义、属性类接口规范JSON格式、属性类接口规范封装 Ajax:
        interface FullName {    // 属性接口
            firstName:string;   // 注意 ; 结束
            secondName?:string;    // 可选属性
        }

        function printName(name:FullName){ // 用到属性接口的方法定义:必须传入对象FullName           
            console.log(name.firstName+'--' + name.secondName);
        }
        // printName('1213');  // 错误,传入参数不合法
        var obj = {   /* 传入的参数必须包含 firstName */
            age:20,    // 其他参数
            firstName:'张',
            secondName:'三'    // 可选参数,可写可不写
        };
        printName(obj)    // 正确的方法调用1
            
            printInfo({    // 正确的方法调用2 参数的顺序可以不一样
                secondName:'四',
                firstName:'李'
            })
——————————————————————————————————————————————————————————————————————————————————
ajax({    // 属性接口应用于 Ajax
    type:'get',
    data:'name=zhangsan',
    url:'http://a.itying.com/api/productlist', //api
    dataType:'json'
})
——————————————————————————————————————————————————————————————————————————————————
interface Config {    // 配置属性接口
    type:string;
    url:string;
    data?:string;
    dataType:string;
}
——————————————————————————————————————————————————————————————————————————————————
function ajax(config:Config) {    // 原生js封装的 Ajax 
   var xhr = new XMLHttpRequest();
   xhr.open(config.type, config.url, true);
   xhr.send(config.data);
   xhr.onreadystatechange = function(){
        if(xhr.readyState == 4 && xhr.status == 200){
            console.log('chengong');
            if(config.dataType == 'json'){
                console.log(JSON.parse(xhr.responseText));
            }else{
                console.log(xhr.responseText)
            }}}}
——————————————————————————————————————————————————————————————————————————————————
// ts中自定义方法传入参数,对 JSON 进行约束 
       function printLabel(labelInfo:{label:string}):void {
            console.log('printLabel');
        }
        printLabel('hahah');  // 错误写法
        printLabel({name:'张三'});  // 错误的写法
        printLabel({label:'张三'});  // 正确的写法

5.2 函数、可索引、类类型接口

  • 函数类型接口:对方法传入的参数、返回值进行约束
  • 可索引接口:数组、对象的约束  (不常用)
  • 类类型接口:对类的约束,类似于抽象类抽象
// 函数类型接口:对方法传入的参数、返回值进行约束    批量约束
interface encrypt{
    (key:string, value:string):string;
}
// 函数 md5 采用 encrypt 接口定义的加密方式
var md5:encrypt = function(key:string,value:string):string{
        return key + value;
}
console.log(md5('name', 'zhangsan'));
——————————————————————————————————————————————————————————————————————————————————
// 可索引接口 对数组的约束
                interface UserArr {    // 对数组的约束
                    [index:number]:string
                }
                // var arr:UserArr = ['aaa','bbb'];
                // console.log(arr[0]);
                var arr:UserArr = [123,'bbb'];  /*错误*/
——————————————————————————————————————————————————————————————————————————————————
// 可索引接口 对对象的约束
                interface UserObj {
                    [index:string]:string
                }
                var arr:UserObj = {name:'张三'};
——————————————————————————————————————————————————————————————————————————————————
// 类类型接口
    interface Animal{
        name:string;
        eat(str:string):void;
    }
    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.3 接口扩展、接口继承

  • 接口可以继承接口
    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.eat();
    w.coding('写ts代码');

6.Typescript 泛型、命名空间

6.1 泛型

  • 泛型就是解决:类、接口、方法的 复用性、以及对不特定数据类型的支持(类型校验)
  • 同时返回 string类型 和 number类型,any可以解决,any放弃了类型检查,传入什么就返回什么
  • T表示泛型,支持不特定的数据类型,具体什么类型是调用方法的时候决定的
// 泛型定义
       function getData<T>(value:T):T{ // 这样就可以同时处理两套数据类型,防止冗余
           return value;
       }
        getData<number>(123);
        getData<string>('1214231');
————————————————————————————————————————————————————————————————————————————————————
// 泛型类
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);
alert(m1.min())
var m2=new MinClas<string>();   /*实例化类 并且指定了类的T代表的类型是string*/
m2.add('a');
m2.add('v');
alert(m2.min())
——————————————————————————————————————————————————————————————————————————————————————
// 泛型接口
        interface ConfigFn<T>{
            (value:T):T;
        }
        function getData<T>(value:T):T{
            return value;
        }
        var myGetData:ConfigFn<string> = getData;     
        myGetData('20');  /*正确*/
        // myGetData(20)  // 错误

      

6.2 命名空间

  • 代码量较大的情况下,为避免变量命名冲突,可将相似功能的函数、类、接口等放置到命名空间内
  • 同 Java 的包、Net 的命名空间一样,TypeScript 的命名空间可以将代码包裹起来,只对外暴露需要在外部访问的对象。命名空间内的对象通过 export 关键字对外暴露
  • 命名空间和模块的区别:
  1. 命名空间:内部模块,主要用于组织代码,避免命名冲突
  2. 模块:ts的外部模块的简称,侧重代码的复用,一个模块里可能会有多个命名空间
  • 命名空间的导入导出也是 export / import
  • modules / animal.ts:
export namespace A { // namespace 命名空间 导出命名空间也采用 export
    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} 吃猫粮。`);
        }}}

export namespace B {...} // B 里可以有和 A 里名字相同的类
  • index.ts
import {A,B} from './modules/animal'; // 导入两个命名空间 import
var d = new A.Dog('小黑');
d.eat();

var dog = new B.Dog('小花');
dog.eat();

7.Typescript 模块、模块封装数据库接口

7.1 模块基本概念

  • 可以把公共功能单独抽离成一个文件作为一个模块,模块里的变量、函数、类等默认是私有的,如果要在外部访问模块里面的数据(变量、函数、类),需要通过 export 暴露模块 里的数据(变量、函数、类...),暴露后通过 import 引入模块,就可以使用模块里暴露的数据(变量、函数、类...)
  • 一些名词区分:
  1. export:在需要导出的变量前面加,也可以简写: export {xx, xx, xx}
  2. 如果是 export,需要加 import {} 引入需要的内容(花括号一定不能忘记)
  3. import {xx as xx}as 对引入的内容重命名
  4. export default 默认导出:每个模块都可以有一个 default 默认导出,并且一个模块只能有一个 default 默认导出
  5. 需要使用一种特殊的导入形式来导入 default 默认导出:就是不加花括号
  • modules/db.ts:
var dbUrl = 'xxxxxx';
export function getData():any[] { 
    console.log('获取数据库的数据111');
    return [
        {
            title:'121312'
        },{
            title:'121312'
        }]
}
export function save() {
    console.log('保存数据成功');
}
// export {dbUrl, getData, save}; // 这是 export 简写
// export default getData; // export default 默认导出
  • index.ts:
import { getData, save } from './modules/db'; // 如果是 export,需要加 {} 引入需要的内容
// import { dbUrl, getData as get} from './modules/db'; // as 对引入的内容重命名
// import getData from './modules/db'; // 这是默认导出 引入的时候不可以添加花括号
getData();
// console.log(dbUrl);

7.2 模块化封装数据库接口

  • 功能:定义一个操作数据库的库,支持 MySql、MsSql、MongoDB
  • 要求:MySql、MsSql、MongoDB 功能一样  都有 add、update、delete、get 方法    
  • 注意:约束统一规范(所以要定义接口)、代码重用(所以用到泛型)
  1. 接口:定义了行为和动作的规范
  2. 泛型:解决 类 接口 方法的复用性
  • modules / db.ts:
// 定义泛型,实现各种复用
interface DBI<T>{
    add(info:T):boolean;
    update(info:T,id:number):boolean;
    delete(id:number):boolean;
    get(id:number):any[];
}
————————————————————————————————————————————————————————————————————————————
// 定义操作 MySql 数据库的类
// 注意:要实现泛型接口,这个类也应该是一个泛型类
export class MysqlDb<T> implements DBI<T> {
    constructor(){
        console.log('数据库建立连接');
    }
    add(info:T):boolean {
        console.log(info);
        return true;
    }    
    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 = [
            {
                title:'xxxx',
                desc:'xxxxxxxxxx'
            },{
                title:'xxxx',
                desc:'xxxxxxxxxx'
            }]
        return list;
    }}
————————————————————————————————————————————————————————————————————————————
// 定义操作 MsSql 数据库的类  
export class MsSqlDb<T> implements DBI<T>{...} // 代码基本同上
  • model / article.ts:
import {MsSqlDb} from '../modules/db';
class ArticleClass{ // 定义数据库映射
    title:string | undefined;
    desc:string | undefined;
}
var ArticleModel = new MsSqlDb<ArticleClass>();
export {
    ArticleClass,
    ArticleModel
}
  • model / user.ts:
import {MsSqlDb} from '../modules/db';
class UserClass { // 定义数据库的映射
    username:string | undefined;
    password:string | undefined;
}
var UserModel = new MsSqlDb<UserClass>();
export {
    UserClass,
    UserModel
}
  • index.ts:
import {UserClass,UserModel} from './model/user';
import {ArticleClass,ArticleModel} from './model/article';

// 增加数据
var u = new UserClass();
u.username = '张三';
u.password = '12345655654757';
UserModel.add(u);

// 获取user表数据
var res = UserModel.get(123);
console.log(res);

// 获取文章表的数据
var aRes = ArticleModel.get(1);
console.log(aRes);

8.Typescript 装饰器

  • 装饰器:一个方法,可以注入到类、方法、属性参数上来扩展类、属性、方法、参数的功能
  • 装饰器分类:类装饰器、属性装饰器、方法装饰器、参数装饰器
  • 装饰器写法:普通装饰器(无法传参)、装饰器工厂(可传参)

8.1 类装饰器

  • 类装饰器:用于类构造函数,可以用来监视、修改或替换类定义
  • 普通装饰器(无法传参):没有 return function() {...}
        // 类装饰器的定义
        function logClass(params:any) {
            console.log(params); // params 就是当前类
            // 在原型链上添加新的属性方法
            params.prototype.apiUrl = '动态扩展的属性';
            params.prototype.run = function(){
                console.log('动态扩展的方法');
            }}

        @logClass // 类装饰器紧挨着类的上面写
        class HttpClient {
            constructor() {}
            getData() {}
        }
        // 实例化类,调用装饰器里新添加的内容
        var http:any = new HttpClient();
        console.log(http.apiUrl); 
        http.run();
  • 装饰器工厂(可传参):有 return function() {...}
        function logClass(params:string) {
            // 装饰器工厂 return function
            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);
  • 重载构造函数:return class extends target {...)
  • 类装饰器表达式会在运行时当作函数被调用,类构造函数为其唯一参数,如果类装饰器返回一个值,它会使用提供的构造函数来替换类的声明
        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();

8.2 属性装饰器

  • 属性装饰器:运行时被当作函数来调用,传入两个参数:
  1. 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象
  2. 成员名字
    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; // url 就是属性成员名字
        constructor() {} // 类的构造函数
        getData() {
            console.log(this.url);
        }}
    var http = new HttpClient();
    http.getData();

8.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.Lyrelion.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 类型
                // 1、保存当前的方法
                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.Lyrelion.com')
            getData(...args:any[]){
                console.log(args);
                console.log('我是getData里面的方法');
            }}

        var http = new HttpClient();
        http.getData(123,'xxx');

8.4 方法参数装饰器

  • 方法参数装饰器:在运行时当作函数被调用,传入三个参数:
  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 );

8.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.Lyrelion.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();

9.Typescript 补充内容

  • npm install -g typescript (在全局环境下安装 tsc 命令)
  • 使用 TypeScript 编写的文件以 .ts 为后缀,用 TypeScript 编写 React 时,以 .tsx 为后缀
  • 运行 tsc hello.ts 出现问题: 
  • 解决方案:以管理员身份 打开windows powershell 输入set-ExecutionPolicy RemoteSigned   选择 A 或者 Y 
  • TypeScript 编译的时候即使报错了,还是会生成编译结果,仍然可以使用这个编译之后的文件
  • 如果要在报错的时候终止 js 文件的生成,可以在 tsconfig.json 中配置 noEmitOnError 

  • boolean 是 JavaScript 中的基本类型
  • Boolean 是 JavaScript 中的构造函数
// 布尔型
let isDone: boolean = false; // 编译成功
let createdByNewBoolean: boolean = new Boolean(1); // 编译失败,使用构造函数创建的不是布尔值
let createdByNewBoolean: Boolean = new Boolean(1); // 编译成功,使用构造函数返回的是布尔对象
let createdByBoolean: boolean = Boolean(1); // 编译成功,直接调用函数,不new,返回布尔值 

// 数字型
let octalLiteral: number = 0o744; // ES6 中的八进制表示法
let notANumber: number = NaN;
let infinityNumber: number = Infinity;

// 字符串型
let myName: string = 'Tom';
let myAge: number = 25;
let sentence: string = `Hello, my name is ${myName}.
I'll be ${myAge + 1} years old next month.`; // 模板字符串
  • 声明为空值 void 的变量,只能被赋值为 null 和 undefined,比如: let unusable: void = undefined;
  • undefined 和 null 可以赋值给 任意类型 的数据,而 void 不可以
  • let num: number = undefined; // 不报错
  • let num: number = void; // 报错
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Lyrelion

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值