Typescript

一、环境配置

1、安装

# npm 安装
npm install -g typescript

# 淘宝安装
# npm install -g cnpm --registry=https://registry.npm.taobao.org
cnpm install -g typescript

# yarn安装
# npm install -g yarn
# cnpm install -g yarn
yarn global add typescript

2、验证安装

tsc -v

3、配置自动编译

  • 创建tsconfig.json文件

    tsc --init
    
  • 修改配置文件

    {
      "compilerOptions": {
        ...
        "outDir": "./js",
        ...
      }
    }
    
  • VSCODE编辑器 -- 终端 -- 运行任务 -- tsc:监视-tsconfig.json(VSCODE新版本)

  • VSCODE编辑器 -- 任务 -- 运行任务 -- tsc:监视-tsconfig.json(VSCODE老版本)

二、数据类型

1、布尔类型(boolean)

var flag:boolean = true;
flag = false;

2、数字类型(number)

var a:number = 123;
a = 12.3;

3、字符串类型(string)

var str:string = 'this is ts'
str = '你好 ts'

4、数组类型(array)

/* 第一种定义数据的方式 */
let arrNumber:number[] = [1,2,3,4]; // 数字数组,只能放数字类型
let arrString:string[]=["java","javascript"]; // 字符串数组,只能放字符串

/* 第二种定义数据的方式 */
let arrNumber:Array<number> = [11,22,33]; // 泛型定义数字数组
let arrString:Array<string> = ["java","javascript"]; // 泛型定义字符串数组

/* 第三种定义数据的方式 */
let arrAny:any[] = [123,'123',true];

5、元组类型(tuple)

// 元组类型(tuple) 属于数组的一种
// 可以在数组中,指定多个数据类型,但是类型要一一对应
let arr:[string,number,boolean] = ["ts",13.18,true];

// let arr:[string,number,boolean] = ["ts",13.18,true,111]; 错误写法
// 不能将类型“[string, number, true, number]”分配给类型“[string, number, boolean]”
// 源具有 4 个元素,但目标仅允许 3 个。

6、枚举类型(enum)

/*
	enum 枚举名 {
		标识符[=整型常数],
		标识符[=整型常数],
		...
		标识符[=整型常数],
	};
*/
// 1、元素赋值,输出被赋予的值
enum Flag {
    success=1,
    error=-1
}
var f:Flag = Flag.success; // 打印f,输出1
// 2、元素不赋值,输出元素索引值,从0开始
enum Color {red,blue,orange};
var c:Color = Color.blue; // 打印c,输出1.
// 3、元素既有赋值,又有未赋值
// 3-1、blue赋值为数字,则orange+1
enum Color {red,blue=5,orange};
Color.blue; // 输出5
Color.orange; // 输出6
// 3-2、blue赋值为字符串,则orange也要为字符串,否则报错
enum Color {red,blue="a",orange="b"};
Color.blue; // 输出 a
Color.orange; // 输出 b

7、任意类型(any)

var num:any = 123;
num = "123"; // 不报错
num = true; // 不报错

8、null和undefined

其他(never类型)数据类型的子类型

var num1:number;
console.log(num1); // 报错

var num2:undefined;
console.log(num2); // 虽然未定值,但是可以运行

var num3:number | undefined;
num3 = 123;
console.log(num3); // 123

var num4:number | undefined;
console.log(num4); // undefined

var num5:null;
num5 = 123; // 报错
num5 = null; // 无报错

var num6:number | undefined | null;
num6 = 123;
console.log(num6); // 123

9、void

/* typescript中的void表示:
 * 		没有任何类型,一般用于定义方法的时候方法没有返回值
 */

// 表示函数没有任何返回类型
function run():void {
    console.log('run')
}

10、never

/*
	never类型:
	是其他类型(包括:null和undefined)的子类型,代表从不会出现的值。
    意味着声明never的变量只能被never类型所赋值
*/

var a:undefined;
a=undefined;

var b:null;
b=null;

var c:never;
// c = 123; // 错误写法
a = (()=>{
    throw new Error('错误');
})()

三、函数

1、函数的定义

/* 函数声明法 */
function run():string {
    return 'run';
}
/* 匿名函数 */
var fun2 = function():number{
    return 123;
}
/* 定义方法传参 */
function getInfo(name:string, age:number):string {
    return `${name} --- ${age}`;
}
var getInfo2 = function(name:string, age:number):string{
    return `${name} --- ${age}`;
}
/* 没有返回值 */
function run:void{
    console.log('run');
}

2、方法可选参数

// es5中方法的实参和形参可以不一样
// ts中必须一样,如果不一样就需要配置可选参数
function getInfo(name:string, age?:number):string {
    if(age) {
        return `${name} --- ${age}`;
    } else {
        return return `${name} --- 年龄保密`;
    }
}

console.log(getInfo('张三',20)) // 张三 --- 20
console.log(getInfo('张三')) // 张三 --- 年龄保密

/* 可选参数必须配置到参数的最后边 */

3、默认参数

// es5里无法设置默认参数
// es6和ts中都可以设置默认参数
function getInfo(name:string, age:number=20):string {
    return `${name} --- ${age}`;
}
console.log(getInfo('张三',30)) // 张三 --- 30
console.log(getInfo('张三')) // 张三 --- 20

4、剩余参数

// 以下方法只能接收4个参数,调用时超出,则报错
function sum(a:number,b:number,c:number,d:number):number {
    return a+b+c+d;
}
sum(1,2,3,4)

// 三点运算符 接收新参传过来的值
function sum(...result:number[]):number {
    var sum = 0;
    for(var i=0;i<result.length;i += 1){
        sum += result[i]
    }
    return sum;
}
sum(1,2,3,4,5,6)

// 改写1
function sum(a:number,...result:number[]):number {
    var sum = a;
    for(var i=0;i<result.length;i += 1){
        sum += result[i]
    }
    return sum;
}
sum(1,2,3,4,5,6)

5、函数重载

// java中的重载:重载指的是两个或两个以上同名函数,但它们的参数不一样,这时会出现函数重载的情况。
// typescript中的重载:通过为同一个函数提供多个函数类型定义来实现多种功能的目的。
// ts为了兼容es5和es6重载的写法和java中有区别

// es5中出现同名方法,下面的会替换上面的方法
function css(config){}
function css(config,value):any{}

// ts重载
function getInfo(name:string);string;
function getInfo(age:number):number;
function getInfo(str:any):any{
    if(typeof str === 'string'){
        return `我叫${str}`;
    } else {
        return str + 1;
    }
}

6、箭头函数

setTimeout(function(){alert('run')},1000)

// 箭头函数中的this指向上下文
setTimeout(()=>{alert('run')},1000)

四、类

1、es5中的类

/* 
	一、最简单的类 
 */
function Person(){
    this.name = '张三';
    this.age = 20;
}
var p = new Person();
console.log(p.name); // 张三

/*********************************************************/

/* 
	二、构造函数和原型链里面增加方法
*/
function Person(){
    this.name = '张三';
    this.age = 20;
    this.run = function() {
        console.log(`${this.name}在运动`)
    }
}
// 原型链上的属性会被多个实例共享,构造函数不会
Person.prototype.sex = '男';
Person.prototype.work = function() {
     console.log(`${this.name}在工作`)
}
var p = new Person();
// run是实例方法,需要new Person()
p.run(); // 张三在运动
p.work(); // 张三在工作

/*********************************************************/

/* 
	三、类中的静态方法
*/
Person.getInfo = function() {
    console.log('我是静态方法');
}
// getInfo是静态方法,无需new,直接调用即可
Person.getInfo(); // 我是静态方法

/*********************************************************/

/* 
	四、es5中的继承
*/
// 定义person类
function Person(){
    this.name = '张三';
    this.age = 20;
    this.run = function() {
        console.log(`${this.name}在运动`)
    }
}
Person.prototype.sex = '男';
Person.prototype.work = function() {
     console.log(`${this.name}在工作`)
}

# 对象冒充实现继承
// web类 继承Person类 
function Web(){
    Person.call(this); // 对象冒充实现继承
}
var w = new Web();
w.run(); // 对象冒充可以继承构造函数中的属性和方法
w.work(); // 报错。对象冒充无法继承原型链上的属性和方法

# 原型链实现继承
// web类 继承Person类 
function Web(){}
Web.prototype = new Person(); // 原型链实现继承
var w = new Web();
w.work(); // 张三在工作

# 原型链确实可以继承Person的构造属性和方法,还有原型链上的属性和方法,但是存在问题。
// 重新定义Person
function Person(name,age){
    this.name = name;
    this.age = age;
    this.run = function() {
        console.log(`${this.name}在运动`)
    }
}
Person.prototype.sex = '男';
Person.prototype.work = function() {
     console.log(`${this.name}在工作`)
}

function Web(){}
Web.prototype = new Person();
# 实例化子类的时候没法给父类传参
var w = new Web('赵四',30); 
w.run(); // undefined在运动

# 原型链 + 构造函数的组合继承模式(总结方式一)
// 定义Person
function Person(name,age){
    this.name = name;
    this.age = age;
    this.run = function() {
        console.log(`${this.name}在运动`)
    }
}
Person.prototype.sex = '男';
Person.prototype.work = function() {
     console.log(`${this.name}在工作`)
}

function Web(name,age){
    Person.call(this,name,age); // 对象冒充继承。实例化子类可以给父类传参
}
Web.prototype = new Person();
var w = new Web('赵四',30); 
w.run(); // 赵四在运动
w.work(); // 赵四在工作

# 原型链 + 构造函数的组合继承模式(总结方式二)
// 定义Person
function Person(name,age){
    this.name = name;
    this.age = age;
    this.run = function() {
        console.log(`${this.name}在运动`)
    }
}
Person.prototype.sex = '男';
Person.prototype.work = function() {
     console.log(`${this.name}在工作`)
}

function Web(name,age){
    Person.call(this,name,age); // 对象冒充继承。实例化子类可以给父类传参
}
Web.prototype = Person.prototype;
var w = new Web('赵四',30); 
w.run(); // 赵四在运动
w.work(); // 赵四在工作

2、typescript中的类

# ts中定义类 1 #
class Person {
    name: string; // 前面省略了public关键字
	
	constructor(n:string){ // 构造函数,实例化类的时候触发的方法
        this.name = n;
    }

	run():void{
        console.log(this.name)
    }
}

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

# ts中定义类 2 #
class Person {
    name: string; // 前面省略了public关键字
	
	constructor(n:string){ // 构造函数,实例化类的时候触发的方法
        this.name = n;
    }

	getName():string {
        return this.name;
    }

	setName(name):void {
        this.name = name;
    }
}
var p = new Person('张三');
console.log(p.getName()) // 张三
p.setName('李四');
console.log(p.getName()) // 李四

3、typescript实现继承

// extends super
class Person {
    name;string;
    constructor(name:string) {
        this.name = name;
    }
    run():string{
        return `${this.name}在运动`;
    }
}

class Web extends Person {
    constructor(name:string){
        super(name);
    }
    work(){
        console.log(`${this.name}在工作`)
    }
}
var w = new Web('李四');
console.log(w.run()); // 李四在运动
w.work(); // 李四在工作

class Web extends Person {
    constructor(name:string){
        super(name);
    }
    run():string{
        return `${this.name}在运动--子类`;
    }
    work(){
        console.log(`${this.name}在工作`)
    }
}
// 字类与父类有同名方法,执行字类自己的方法
console.log(w.run()); // 李四在运动--子类

4、typescript类中的修饰符

修饰符 类中是否可访问 子类是否可访问 类外是否可访问
public
protected
private

5、typescript的静态属性和静态方法

class Person {
    public name: string;
	static gender:string = "男"; // 静态属性
	constructor(name:string){
        this.name = name;
    }
	run():string {
        return `${this.name}在运动`;
    }
	static print() { // 静态方法
		console.log('print')
	}
}
Person.print(); // print
console.log(Person.gender); // 

6、typescript的多态

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

// 动物类
class Animal {
    name:string;
    constructor(name:string){
        this.name = name;
    }
    eat(){
        console.log('eat')
    }
}
// 狗
class Dog extends Animal {
    constructor(name:string){
        super(name);
    }
    eat(){
        return this.name + '吃肉'
    }
}
// 猫
class Cat extends Animal {
    constructor(name:string){
        super(name);
    }
    eat(){
        return this.name + '吃鱼'
    }
}

7、typescript的抽象方法

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

abstract class Animal{
    name:string;
    constructor(name:string){
        this.name = name;
    }
    abstract eat():any;
}

class Dog extends Animal {
    constructor(name:string){
        super(name);
    }
    // 抽象类的子类必须实现抽象类里面的抽象方法
    eat(){
        console.log(this.name + '吃肉')
    }
}

五、接口

1、属性接口

// 对json的约束

/* 对传入对象的约束 属性接口 */
interface Fullname {
    firstName:string; // 分号结束
    secondName:string;
}

function printName(name: Fullname){
    // 必须传入对象 firstName secondName
    console.log(name.firstName + '---' + name.secondName)
}

/* 对批量方法进行约束 */
printName({
    firstName:'张',
    secondName:'三',
    age:30
}) // 报错
printName({
    firstName:'张',
    secondName:'三'
}) // 正常
var obj = {
    firstName:'张',
    secondName:'三',
    age:30
}
printName(obj) // 正常

1-1、可选属性

interface Fullname {
    firstName:string; // 分号结束
    secondName?:string;
}
function printName(name: Fullname){
    // 必须传入对象 firstName secondName
    console.log(name.firstName + '---' + name.secondName)
}
printName({
    firstName:'张'
}) // 正常。无需再传secondName。因为secondName可选

1-2、案例

/* 原生js封装ajax,不兼容IE6 */

// ajax请求数据接口
interface Config {
    type: string;
    url:string;
    data?:string;
    dataType:string;
}

function ajax(config: Config) {
    var xht = 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('成功');
            if(config.dataType == 'json'){
                console.log(JSON.parse(xhr.responseText))
            }else {
                console.log(xhr.resposeText)
            }
        }
    }
}

ajax({
    type: 'get',
    url: 'http://api',
    dataType: 'json'
})

ajax({
    type: 'get',
    data: 'name=zhangsan'
    url: 'http://api',
    dataType: 'json'
})

2、函数类型接口

// 函数类型接口:对方法传入的参数以及返回值进行约束 批量约束

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

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

console.log(md5('name','zhangsan'))

3、可索引接口

# 可索引接口:数组、对象的约束(不常用)

// 对数组约束
interface UserArr {
    [index:number]:string
}
var arr:UserArr = ['123','124']

// 对对象约束
interface UserObj {
    [index:string]:string
}
var arr:UserObj = {name:'张三'}

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;
	contructor(name:string) {
        this.name = name;
    }
	eat(food:string){
        console.log(this.name + '吃' + food)
    }
}

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

5、接口扩展

# 接口扩展:接口可以继承接口

interface Animal{
    eat():void;
}

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

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

var w = new Web('小磊');
w.work();
w.eat();
# extends implements 结合使用

interface Animal{
    eat():void;
}

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

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

class Web extends programmer implements Person {
    contructor(name:string){
        super(name)
    }
    eat(){
        console.log(this.name+'喜欢吃馒头')
    }
	work(){
        console.log(this.name+'敲代码')
    }
}
var w = new Web('小磊');
w.coding('写ts代码')

六、泛型

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类型 (可能传入number,但是返回string。放弃了类型检查)
function getData3(value:any):any {
    return value;
}
// 泛型:可以支持不特定的数据类型。要求:传入的参数和返回的参数一致
// T表示泛型,具体什么类型是调用方法的时候决定的
function getData3<T>(value:T):T {
    return value;
}
getData<string>('abc')
getData<number>(123)

2、泛型类

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

// 只支持number的MinClass
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+=1){
            if(minNum > this.list[i]){
                minNum = this.list[i];
            }
        }
        return minNum;
    }
}
var m = new MinClass();
m.add(2);
m.add(3);
m.add(4);
console.log(m.min);

// 泛型改造
class MinClass<T> {
    public list:T[]=[];
	add(num:T) {
        this.list.push(num)
    }
	min():T{
        var minNum = this.list[0];
        for(var i=0;i<this.list.length;i+=1){
            if(minNum > this.list[i]){
                minNum = this.list[i];
            }
        }
        return minNum;
    }
}

3、泛型接口

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

// 泛型改造1
interface Configfn {
    <T>(value1:T):T
}
var getData:Configfn = function<T>(value:T):T{
    return value;
}
getData<string>('张三')

// 泛型改造2
interface Configfn {
    <T>(value1:T):T
}
function getData<T>(value:T):T{
    return value;
}
var myGetData:Configfn<string>=getData;
myGetData('20')

4、泛型类拓展

// 把类作为参数来约束数据传入的类型
class User {
    username: string | undefined; // 为保证username不被赋值时不报错,需要加入undefined类型
	password: string | undefined;
}

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

// 泛型改造
class Article<T> {
    title: string | undefined;
	describe: string | undefined;
	status: number | undefined;
}
class MysqlDb {
    add(info:T):boolean {
        return true;
    }
}
var a = new Article();
a.title = '标题';
a.describe = '描述';
a.status = 1;
var Db = new MysqlDb<Article>();
Db.add(a);

七、综合使用

1、封装统一操作(Mysql|Mongodb|Mssql)

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> {
    add(info: T): boolean {
        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[] {
        throw new Error("Method not implemented.");
    }
}

class MsSqlDb<T> implements DBI<T> {
    add(info: T): boolean {
        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[] {
        throw new Error("Method not implemented.");
    }
}

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

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

var omysql = new MysqlDb<User>();
omysql.add(u);

八、模块

// ./modules/db.ts
var dbUrl = 'xxx';
export function getData():any[] {
    console.log('获取数据库数据');
    return [
        {title:'123'},
        {title:'124'}
    ]
}
export function save():void {
    console.log('保存成功')
}

// 引入
import { getData,save } from './modules/db'
getData();
save();
// 另外一种暴露数据的方法
// ./modules/db.ts
var dbUrl = 'xxx';
function getData():any[] {
    console.log('获取数据库数据');
    return [
        {title:'123'},
        {title:'124'}
    ]
}
function save():void {
    console.log('保存成功')
}
export {dbUrl,getData,save}

// 引入
import { getData as get,save } from './modules/db'
get();
save();
// export default:只能用一次
// ./modules/db.ts
var dbUrl = 'xxx';
function getData():any[] {
    console.log('获取数据库数据');
    return [
        {title:'123'},
        {title:'124'}
    ]
}
function save():void {
    console.log('保存成功')
}
export default getData;

// 引入
import getData from './modules/db'
getData();

九、命名空间

// 命名空间:内部模块,主要用于组织代码,避免命名冲突
// 模块:ts的外部模块的简称,侧重代码的复用,一个模块里可能会有多个命名空间
namespace A {
    interface Animal {
        name: string | undefined;
        eat(): void;
    }

    export class Dog implements Animal {
        name: string | undefined;
        constructor(theName:string){
            this.name = theName;
        }
        eat(): void {
            console.log(`${this.name}吃狗粮`);
        }
    }

    export class Cat implements Animal {
        name: string | undefined;
        constructor(theName:string){
            this.name = theName;
        }
        eat(): void {
            console.log(`${this.name}吃猫粮`);
        }
    }
}

namespace B {
    interface Animal {
        name: string | undefined;
        eat(): void;
    }

    export class Dog implements Animal {
        name: string | undefined;
        constructor(theName:string){
            this.name = theName;
        }
        eat(): void {
            console.log(`${this.name}吃狗粮`);
        }
    }

    export class Cat implements Animal {
        name: string | undefined;
        constructor(theName:string){
            this.name = theName;
        }
        eat(): void {
            console.log(`${this.name}吃猫粮`);
        }
    }
}

var d = new A.Dog('狗'); // 命名空间A的Dog类
d.eat();

var c = new B.Cat('猫'); // 命名空间B的Cat类
c.eat();

9-1、模块化

export namespace A {
    interface Animal {
        name: string | undefined;
        eat(): void;
    }

    export class Dog implements Animal {
        name: string | undefined;
        constructor(theName:string){
            this.name = theName;
        }
        eat(): void {
            console.log(`${this.name}吃狗粮`);
        }
    }

    export class Cat implements Animal {
        name: string | undefined;
        constructor(theName:string){
            this.name = theName;
        }
        eat(): void {
            console.log(`${this.name}吃猫粮`);
        }
    }
}

export namespace B {
    interface Animal {
        name: string | undefined;
        eat(): void;
    }

    export class Dog implements Animal {
        name: string | undefined;
        constructor(theName:string){
            this.name = theName;
        }
        eat(): void {
            console.log(`${this.name}吃狗粮`);
        }
    }

    export class Cat implements Animal {
        name: string | undefined;
        constructor(theName:string){
            this.name = theName;
        }
        eat(): void {
            console.log(`${this.name}吃猫粮`);
        }
    }
}
    
// 引入
import {A} from './a'
var d = new A.Dog('狗');
d.eat();

十、装饰器

1、普通装饰器(不带参数)

// 装饰器
function logClass(params:any){
    console.log(params); // params就是当前类
    params.prototype.apiUrl = 'urlxxx';
    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://api')
class HttpClient {
    constructor(){}
    getData(){}
}

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

3、修改类中的构造函数和构造方法

function logClass(target:any){
    console.log(target);
    // 重写HttpClient函数的构造函数和构造方法
    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();

4、属性装饰器

// 属性装饰器表达式会在运行时当作函数被调用,传入下列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('http://api')
class HttpClient {
    @logProperty('http://api')
    public url:any | undefined;
    constructor(){}
    getData(){
        console.log(this.url);
    }
}

var http = new HttpClient();
http.getData();

5、方法装饰器

// 发放装饰器
// 应用到方法的属性描述符上,可以用来监视,修改或者替换方法定义
// 方法装饰会在运行时传入下列3个参数
//		1、对于静态成员来说是类的构造函数,对于实例成员是类的原型对象
//		2、成员的名字
//		3、成员的属性描述符

function get(params:any){
    return function(target:any,methodName:any,describe:any){
        console.log(target);
        console.log(methodName);
        console.log(describe);

        target.apiUrl = 'xxxx';
        target.run = function(){
            console.log('run');
            
        }
    }
}

class HttpClient {
    public url:any | undefined;
    constructor(){}
    @get('http://www.baidu.com')
    getData(){
        console.log(this.url);
    }
}

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

5-1、修改方法

function get(params:any){
    return function(target:any,methodName:any,describe:any){
        console.log(target);
        console.log(methodName);
        console.log(describe.value);
        // 修改装饰器方法 把装饰器方法里面传入的所有参数改为stringl类型
        // 保存当前的方法
        var oMethod = describe.value;
        describe.value = function(...args:any[]){
            args = args.map((value)=>{
                return String(value);
            })
            console.log(args);
        }
    }
}

class HttpClient {
    public url:any | undefined;
    constructor(){}
    @get('http://www.baidu.com')
    getData(){
        console.log('我是getData的方法');
    }
}

var http:any = new HttpClient();
http.getData(123,'xxxx');

6、方法参数装饰器

// 方法参数装饰器
// 参数装饰器表达式会在运行时当作函数被调用,可以使用参数装饰器为类的原型增加一些元素数据,传入下列3个参数:
// 	1、对于静态成员来说是类的构造函数,对于实例成员是类的原型对象
//	2、方法的名字
// 	3、参数在函数参数列表中的索引
function logParams(params:any){
    console.log(params);
    return function(target:any,methodName:any,paramIndex:any){
        console.log(target);
        console.log(methodName);
        console.log(paramIndex);

        target.apiUrl = params;
    }
}
class HttpClient {
    public url:any | undefined;
    constructor(){}
    getData(@logParams('xxxxx') uuid:any){
        console.log('我是getData的方法');
    }
}

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

7、装饰器的执行顺序

function logClass1(params:string){
    return function(target:any){
        console.log('类装饰器1');
    }
}
function logClass2(params:string){
    return function(target:any){
        console.log('类装饰器2');
    }
}
function logAttribute(params?:string){
    return function(target:any,attrName:any){
        console.log('属性装饰器');
    }
}

function logMethod(params?:string){
    return function(target:any,methodName:any,describe:any){
        console.log('方法装饰器');
    }
}

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

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

@logClass1('class1')
@logClass2('class2')
class HttpClient {
    @logAttribute()
    public url:any | undefined;
    constructor(){}
    @logMethod()
    getData(){
        return true;
    }
    setData(@logParams1() attr1:any,@logParams2() attr2:any){}
}

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


属性 》 方法 》 方法参数 》 类
如果有多个同样装饰器,会先执行后边的

学习自Typescript教程_Typescript视频教程 ts入门实战视频教程-2020年4月更新

原文地址:https://www.cnblogs.com/luckyzs/p/14212882.html