您现在的位置是:首页 >学无止境 >TypeScript自学文档网站首页学无止境

TypeScript自学文档

慎思笃行_ 2023-05-28 16:00:02
简介TypeScript自学文档

目录

1.什么是Ts?

1.1 设计公司:微软

1.2 TS概述

1.3 TS是静态类型 JS是动态类型

1.4 TS是强类型语言 JS是弱类型语言

2.TypeScript编译器

2.1 安装

 2.2 TS自动编译和编译选项设置

3.TS的数据类型

3.1 基础数据类型number、string、boolean

3.2 Arrays(数组类型)

3.3 any(任意类型)

3.4 unknown(类型未知)

3.5 void(空值)了解

3.6 never

3.7 TS中的类型推断

3.8 Functions (函数)

3.9 Object(对象类型)

3.10 联合类型

3.11 类型别名(Type Alias)

3.12 类型语法(Interfaces)

3.13 类型断言(Type Assertion)

3.14 字面类型(Literal Types)

3.15 元组(tuple) 

3.16 枚举(Enum)

3.17 不常用类型 了解即可

3.18 类型过滤(Type Narrowing)集中数据筛选方式 感兴趣可以看下

4、TS函数

4.1函数格式:

5、class 类

5.1 类的定义

5.2 类的继承

5.3 修饰符

5.4 静态属性和方法

5.5 抽象类

5.6 多态(Polymorphism)

6.TS的接口

6.1 接口的定义

6.2 接口的用途

6.3 属性类型接口

6.4 函数类型接口

6.5 可索引性接口 了解

6.6 类类型接口

6.7接口的继承

7、TS泛型

7.1 泛型类

7.2 泛型类接口

8、TS修饰器

8.1 类装饰器 ClassDecorator(不可传参)

8.2装饰器工厂 (也可以叫柯里化,闭包,可传参)

8.3 属性装饰器

8.3 方法装饰器 MethodDecorator

8.4 各种装饰器的执行顺序


1.什么是Ts?

1.1 设计公司:微软

1.2 TS概述

官方介绍:TypeScript 是 JavaScript 的超集,这意味着它可以完成 JavaScript 所做的所有事情,而且额外附带了一些能力。
Typed JavaScript at Any Scale. 它强调了 TypeScript 的两个最重要的特性——类型系统、适用于任何规模。

(简述:JavaScript应用广泛,但是由于其为弱类型语言,使其在项目管理层面表现得相当差劲。科技巨头微软为了解决这个尴尬的问题,推出了typeScript)

1.3 TS是静态类型 JS是动态类型

静态类型:编写时要注意变量、函数的数据类型,并且在编译时就对代码类型检测并立即报错。

动态类型:编写时不需要注意变量、函数的数据类型,只有程序运行后才对代码类型检测并报错。

总体来说,静态类型语言更适合大型项目,可以减少错误并提高代码质量;而动态类型语言则更适合小型或快速开发项目,可以增加开发效率。

1.4 TS是强类型语言 JS是弱类型语言

弱类型:编程时变量可以不声明数据类型,并在赋值或运算时数据类型可以随意转换。

强类型:编程时必须明确声明数据类型,如果发现类型错误,就会在编译或运行期间发生错误。

2.TypeScript编译器

2.1 安装

tsc是TypeScript的编译器,我们一起来安装一下吧!

//安装ts编译器
npm i -g typescript

//检查是否安装成功
tsc

//ts文件编译变为js文件  (helloTS.ts这个是文件名)
tsc helloTS.ts

//ts文件编辑中  遇到无法重新声明块范围变量“*****”  (生成tsconfig.json文件就可以解决了)
tsc --init

tsc helloTS.ts 编译前

 tsc helloTS.ts 编译后

 2.2 TS自动编译和编译选项设置

2.2.1 局部-自动编译指定文件

tsc (文件名) -w  可以对指定文件自动编译

tsc helloTS1.ts -w

2.2.2 全局-自动编译文件

tsc -w

提及下tsconfig.json相关设置 (有助于自定义全局自动编译文件)

{
  // include是指定要编译哪些ts文件
 "include":[
  "./src/**/*"
 ],
//  exclude表示不包含
 "exclude":[
  "./hello/**/*"
 ],
//compilerOptions编译器的选项
"compilerOptions": {
  // target用来指定ts被编译为的Es的版本
  "target":"ES6",
  //module 指定要使用的模块化的规范
  "module": "ES6",
  //用来指定项目中要使用的库
  "lib":["es6","dom"],
  //outDir用来指定编译后文件所在的目录
  "outDir": "./dist",
  //将代码合并为一个文件
  //设置outFile后,所有的全局作用域中的代码会合并到同一个文件中
  "outFile": "./dist/app.js",
  //是否对js文件进行编译 默认是false
  "allowJs":false,
  //是否检查js代码是否符合语法规范 默认值是false
  "checkJs": false,
  //是否移除注释 默认为false
  "removeComments": false,
  //是否生成编译后的文件 
  "noEmit": false,
   //当有错误的时不生成编译后的文件 
   "noEmitOnError": false,
   //用来设置编译后的js文件是否设置严格模式
   "alwaysStrict": false,
   //不允许隐式的any类型
   "noImplicitAny": true,
  //不允许明确类型的this
  "noImplicitThis": true,
  //严格的检查空值
  "strictNullChecks": false,
},
}

3.TS的数据类型

类型例子描述
number1,-33,2.5任意数字
string“hi”,“hi”,hi任意字符串
booleantrue,false布尔值true或false
字面量其本身限制变量的值就是该字面量的值
any*任意类型
unknown*类型安全的any
void空值(undefined)没有值(或undefined)
never没有值不能是任何值
object{name:‘孙悟空’}任意的JS对象
array[1,2,3]任意JS数组
tuple[4,5]元素,TS新增类型,固定长度数组
enumenum{A,B}枚举,TS中新增类型

3.1 基础数据类型number、string、boolean

let a:number;  //数值
a = 18;
console.log(a,"number");

let b:string;  //字符串
b = 'wengzi';
console.log(b,"string");

let c:boolean;  //布尔
c = true;
console.log(c,"boolean");

3.2 Arrays(数组类型)

  • 使用数组字面量语法:可以用方括号 [] 括起来并以逗号分隔的一组值来表示一个数组。
let arr:number[];  //数组
arr=[1,2,3,4,5,6]
console.log(arr,"array");
  • 使用数组构造器:可以使用 Array 构造器来创建一个数组。
let arr1:Array<number>;  //数组
arr1=[1,2,3,4,5,6]
console.log(arr1,"array1");

3.3 any(任意类型)

any类型表示任意类型,它可以在编译时不进行类型检查和类型推断,允许我们随意地对其进行读写操作。使用any类型通常是为了处理一些动态类型、非常规类型或者无法确定类型的值。

let d: any;
d = 123;     // 赋值为数字
d = "hello"; // 赋值为字符串
d = true;    // 赋值为布尔值
console.log(d, "任意类型");

3.4 unknown(类型未知)

概念:一个安全的any,一个类型为unknown的变量,不能直接赋值给其他变量

小提示:unknown 数据类型的数据要赋值的话,得使用类型断言 详情请看->  3.13类型断言

3.5 void(空值)

void 用来表示空,用作函数的返回值类型,表示函数没有返回值。除了将void类型作为函数返回值类型外,在其他地方使用void类型是无意义的

function fn():void{
    
}

3.6 never

never类型可以作为函数的返回值类型,表示该函数无法返回一个值。

function fn2():never{
    throw new Error('报错了!!!');
    //该函数永远无法执行到末尾,返回值类型为"never"
}

3.7 TS中的类型推断

ts也可以根据赋值类型,推断出来变量的类型。

let e = "ceshi";
console.log(e, "类型推断");

3.8 Functions (函数)

function add(x: number, y: number): number {
  return x + y;
}
//调用函数
const result = add(10, 30);
console.log(result, "函数");

3.9 Object(对象类型)

  • 与js的写法类似,以下是一个简单的字面量创建对象
//使用对象字面量创建对象
const person = {
  name: "xiaoming",
  age: 18,
  gender: "female",
};
console.log(person, "对象");
  • 也可以通过接口(interface)来进行定义和约束
//定义一个person1接口
interface person1 {
  name: string;
  age: number;
  gender: string;
}

//使用接口定义对象
const person2: person1 = {
  name: "xiaohong",
  age: 20,
  gender: "female",
};
console.log(person2, "对象");

3.10 联合类型

TypeScript中的联合类型使用 | 符号将几种不同类型组合成一个新的类型。表示该变量、参数或属性可以是这些集合中任何一种类型之一

  • 定义一个联合类型变量
let numOrStr:number|string;
numOrStr=100;  //数值
numOrStr='Hello world'; //字符串
console.log(numOrStr,"联合类型");
  • 函数中的使用
function printId(id: number | string) {
  console.log("联合类型", id);
}
printId(11);
printId("文字");

小提示:

开发中会因为联合类型导致toUpperCase()方法无法使用

 解决方法如下:

(我们通过一定的条件过滤将类型做了划分,最终解决了类型交叉的问题。)

function printId(id: number | string) {
  if (typeof id === "string") {
    // In this branch, id is of type 'string'
    console.log(id.toUpperCase());
  } else {
    // Here, id is of type 'number'
    console.log(id);
  }
}

3.11 类型别名(Type Alias)

定义一个变量

type bieming = number | string;
let bm: bieming;
bm = 123;
bm = "aaa";
console.log(bm, "类型别名");

3.12 类型语法(Interfaces)

//定义一个person1接口
interface person1 {
  name: string;
  age: number;
  gender: string;
}

//使用接口定义对象
const person2: person1 = {
  name: "xiaohong",
  age: 20,
  gender: "female",
};
console.log(person2, "类型语法");

3.13 类型断言(Type Assertion)

概念:可以强制将一个值的类型转化为开发者想要的类型

  • 尖括号语法
let someValue:any="hello world"
let ceshiValue:number=(<string>someValue).length
console.log(ceshiValue,"类型断言");
  • as语法
let someValue:any="hello world"
let ceshiValue:number=(someValue as string).length
console.log(ceshiValue,"类型断言");

小提示:实际开发过程中会碰到有些类型没办法直接断言的情况,那么我们就需要断言两次,通常会使用as any或者as unknown作为第一层断言“(expr as any) as T

3.14 字面类型(Literal Types)

概念:定义一个字面类型,以后赋值只能内容 === 恒等

type myString='hello';  //定义一个字符串字面类型
type myNumber=123;      //定义一个数值字面类型
type myBoolean=true;   //定义一个布尔字面类型

let myVar1: myString = 'hello'; // 可以赋值为 'hello'
let myVar2: myString = 'world'; // 报错,只能是 'hello'

let myVar3: myNumber = 123; // 可以赋值为 123
let myVar4: myNumber = 456; // 报错,只能是 123

let myVar5: myBoolean = true; // 可以赋值为 true
let myVar6: myBoolean = false; // 报错,只能是 true

再来一个联合类型的例子

function printText(s: string, alignment: "left" | "right" | "center") {
  // ...
}
printText("Hello, world", "left");
printText("G'day, mate", "centre");

再来一个联合类型的例子

function compare(a: string, b: string): -1 | 0 | 1 {
  return a === b ? 0 : a > b ? 1 : -1;
}

3.15 元组(tuple) 

概念:元组,就是固定长度的数组,即其中的元素个数是固定的。

let h :[string,number];
h = ['a',10]

3.16 枚举(Enum)

概念:是一种常用的数据类型,用于表示固定的预定义值集合

enum Color{
    Red=1,
    Green,
    Blue,
}
console.log(Color.Red);  //输出1
console.log(Color.Green);//输出2
console.log(Color.Blue); //输出3

console.log(Color[1]);  //输出'Red'
console.log(Color[2]);  //输出'Green'
console.log(Color[3]);  //输出'Blue'

在上述代码中,我们定义了一个名为 Color 的枚举类型,并设置了三个枚举项,分别对应的数值依次为 1, 23。枚举项的名称可以通过 . 访问,也可以通过枚举值反向查找到对应的名称。

需要注意的是,如果不显式为枚举项指定数值,则它们将会从 0 开始依次递增。如果第一个枚举项指定了数值,则它之后的枚举项将在此基础上增加。

枚举是一种比较简单、直观的数据类型,可以用于表示有限的取值范围和状态等。特别是在开发过程中定义一些固定的常量时,枚举可以起到很好的作用,并提高代码的可读性和可维护性。

3.17 不常用类型 了解即可

bigint

从ES2020开始,JavaScript中有一个用于非常大整数的原语BigInt:

// Creating a bigint via the BigInt function
const oneHundred: bigint = BigInt(100);
 
// Creating a BigInt via the literal syntax
const anotherHundred: bigint = 100n;

symbol

JavaScript中有一个原语,用于通过函数Symbol()创建全局唯一引用:

const firstName = Symbol("name");
const secondName = Symbol("name");
 
if (firstName === secondName) {
This condition will always return 'false' since the types 'typeof firstName' and 'typeof secondName' have no overlap.
  // Can't ever happen
}

3.18 类型过滤(Type Narrowing)集中数据筛选方式 感兴趣可以看下

概念:使用断言、判断和匹配等手段,在特定条件下缩小一个变量的类型范围。常用的类型过滤技巧包括条件语句、类型保护函数和 in 操作符等。

  • 条件语句:
let value: string | number;
if (typeof value === 'string') {
  console.log(value.toUpperCase()); // OK,字符串类型
} else {
  console.log(value.toExponential()); // OK,数值类型
}
  • 类型保护函数:
function isPerson(obj: any): obj is Person {
  return typeof obj.name === 'string' && typeof obj.age === 'number';
}

type Person = { name: string, age: number };

function greet(person: Person | string) {
  if (isPerson(person)) {
    console.log(`Hello, ${person.name}!`); // OK,可以直接访问 person 的属性
  } else {
    console.log(`Hello, ${person}!`);
  }
}

greet({ name: "Alice", age: 25 }); // 输出:Hello, Alice!
greet("World"); // 输出:Hello, World!
  • in 操作符
type Shape = Square | Circle;

interface Square {
  width: number;
}

interface Circle {
  radius: number;
}

function area(shape: Shape) {
  if ('width' in shape) { // 判断作为 Square 类型处理
    return shape.width ** 2;
  } else { // 判断作为 Circle 类型处理
    return Math.PI * shape.radius ** 2;
  }
}

console.log(area({ width: 5 })); // 输出:25
console.log(area({ radius: 1.5 })); // 输出:7.0685834705770345

4、TS函数

4.1函数格式:

  • 格式一
function 函数名(参数列表): 返回值类型 {
    函数体 ...
    [return 返回值;]
}
  • 格式二

let 函数名 = function (参数列表): 返回值类型 {
    函数体 ...
    [return 返回值;]
};

常见写法

  • 必选参数写法

function getInfo(name:string,age:number):string{
    return `${name}---${age}`
}
console.log(getInfo('小明',1)); //正确
console.log(getInfo('小红')); //错误
console.log(getInfo(1)); //错误
  • 可选参数写法
function getInfo(name:string,age?:number):string{
    return `${name}---${age}`
}
console.log(getInfo('小明',1)); //正确
console.log(getInfo('小红')); //正确
console.log(getInfo(1)); //错误
  • 默认参数


function getInfo(name:string,age:number=20):string{
    return `${name}---${age}`
}
console.log(getInfo('小明',30)); //正确  小明---30
console.log(getInfo('小红')); //正确  小红---20
console.log(getInfo(1)); //错误
  • 剩余参数

剩余参数:在参数的类型确定而参数个数不确定的情况时,我们需要用到剩余参数,它使用 ... 将接收到的参数传到一个指定类型的数组中。

function sum(...result:number[]):number{
    let sum=0;
    for(let i=0;i<result.length;i++){
        sum+=result[i];
    }
    return sum;
}
console.log(sum(1,2,3,4,5,6));
  • 箭头函数

setTimeout(function () {
    console.log("匿名函数执行了...");
}, 1000);

setTimeout(() => {
    console.log("箭头函数执行了...");
}, 1000);

5、class 类

5.1 类的定义

class Person{
    name:string;
    constructor(n:string){  //constructor是用来定义类的构造函数
        this.name=n;
    }
    run():void{
        console.log(this.name+"在图书馆"); 
    }
}
let p=new Person('王哈哈');
p.run();

5.2 类的继承

类的继承:在TypeScript中想要实现继承使用extends关键字

class Person    {
    name: string;//父类属性,前面省略了public关键词

    constructor(n: string) {//构造函数,实例化父类的时候触发的方法
        this.name = n;//使用this关键字为当前类的name属性赋值
    }

    run(): void {//父类方法
        console.log(this.name + "在跑步");
    }
}

//中国人这个类继承了人这个类
class Chinese extends Person {
    age: number;//子类属性

    constructor(n: string, a: number) {//构造函数,实例化子类的时候触发的方法
        super(n);//使用super关键字调用父类中的构造方法
        this.age = a;//使用this关键字为当前类的age属性赋值
    }

    speak(): void {//子类方法
        super.run();//使用super关键字调用父类中的方法
        console.log(this.name + "说中文");
    }
}

var c1 = new Chinese("张三", 28);
c1.speak();

5.3 修饰符

TypeScript 里面定义属性的时候给我们提供了 三种修饰符

  • public: 默认的修饰符,可以被类的实例、子类和其他类访问。

  • private: 只能被定义它的类所访问,在其它地方无法进行访问。

  • protected: 可以被定义它的类和该类的子类所访问,但是在类外部无法访问。

class Person    {
    private name:string;  //private: 只能被定义它的类所访问,在其它地方无法进行访问。
    constructor(n: string) {
        this.name = n;
    }

    run(): void {
        console.log(this.name + "在跑步");
    }
}
class Chinese extends Person {
    age: number;

    constructor(n: string, a: number) {
        super(n);
        this.age = a;
    }

    speak(): void {
        super.run();
        console.log(this.name + "说中文");  //报错
    }
}
var c1 = new Chinese("张三", 28);
c1.speak();

5.4 静态属性和方法

static:用于声明静态成员或方法。静态成员不需要实例化即可访问。

class MathUtility {
  public static readonly PI: number = 3.14159; // 只读静态成员

  public static add(a: number, b: number): number { // 静态方法,用于求和
    return a + b;
  }
}

const sum = MathUtility.add(4, 5); // 直接通过类名调用静态方法
console.log(sum); // 输出 9
console.log(MathUtility.PI); // 直接通过类名访问只读静态变量

5.5 抽象类

抽象类是一种不能被实例化的类,因为它仍然需要子类来继承并实现其方法。抽象类中可以声明抽象方法,这些方法必须在子类中被覆盖/实现,否则子类将无法被编译通过。

//动物抽象类,所有动物都会跑(假设),但是吃的东西不一样,所以把吃的方法定义成抽象方法
abstract class Animal {
    name: string;
    constructor(name: string) {
        this.name = name;
    }
    abstract eat(): any;//抽象方法不包含具体实现并且必须在派生类中实现
    run() {
        console.log(this.name + "会跑")
    }
}

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

var d: Dog = new Dog("小狼狗");
d.eat();

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

var c: Cat = new Cat("小花猫");
c.eat();

5.6 多态(Polymorphism)

概念:指一个类的实例可以作为另一个类的实例使用。具体来说,多态允许你编写更通用、可重用和可扩展的代码,因为它支持将不同的子类对象赋值给共同的父类对象,而这些子类对象会表现出不同的行为。

class Animal {
    constructor(public name: string) {}
    makeSound() {
      console.log(` ${this.name}  会叫`);
    }
  }

  class Dog extends Animal {
    constructor() {
      super('dog');
    }
    makeSound() { // 覆盖基类方法
      console.log('汪汪!');
    }
  }
  
  class Cat extends Animal {
    constructor() {
      super('cat');
    }
    makeSound() { // 覆盖基类方法
      console.log('喵喵!');
    }
  }
  
  function animalSounds(animals: Animal[]) {
    animals.forEach(animal => {
      animal.makeSound(); // 多态调用 makeSound 方法
    });
  }
  const dog = new Dog();
  const cat = new Cat();
  const animals = [dog, cat];
  animalSounds(animals); // 输出 '汪汪!' 和 '喵喵!'

6.TS的接口

6.1 接口的定义

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

6.2 接口的用途

接口的用途就是对行为和动作进行规范和约束,跟抽象类有点像,但是接口中不可以有方法体,只允许有方法定义。

6.3 属性类型接口

//对传入对象的属性约束,以下这个是一个属性接口
interface FullName {
    message: string;
    num: number;
}
function printName(name: FullName) {
    console.log(name.message + "--" + name.num);
}
//传入的参数必须包含message、num
var obj = {
    age: 20,
    message: '你好',
    num: 123
};
printName(obj);//正确
// printName("1");//错误

6.4 函数类型接口

//加密的函数类型接口
interface SearchFunc {
    (source: string, subString: string): string;
  }
let m: SearchFunc = function (key: string, value: string): string {
    //模拟操作
    return key + "----" + value;
}
console.log(m("name", "王哈哈"));

6.5 可索引性接口 了解

//可索引接口,对数组的约束
interface UserArr {
    [index: number]: string
}
var arr1: UserArr = ["aaa", "bbb"];
console.log(arr1[0]);

//可索引接口,对对象的约束
interface UserObj {
    [index: string]: string
}
var arr2: UserObj = { name: '张三', age: '21' };
console.log(arr2);

6.6 类类型接口

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("大老鼠");

6.7接口的继承

//人这个接口
interface Person {
    eat(): void;
}

//程序员接口
interface Programmer extends Person {
    code(): void;
}

//小程序接口
interface Web {
    app(): void;
}

//前端工程师
class WebProgrammer implements Person, Web {
    public name: string;
    constructor(name: string) {
        this.name = name;
    }
    eat() {
        console.log(this.name + "下班吃饭饭")
    }
    code() {
        console.log(this.name + "上班敲代码");
    }
    app() {
        console.log(this.name + "开发小程序");
    }
}

var w = new WebProgrammer("小李");
w.eat();
w.code();
w.app();

7、TS泛型

概念:泛型是一种用于编写可重复使用的代码的工具,它可以让我们在编译时期将类型作为参数传递给函数或类。通过使用泛型,我们可以编写更灵活和更通用的代码,并且可以避免类型重复定义和类型强制转换的问题。

//创建一个通用的 identity 函数
function identity<T>(arg: T): T {
    return arg;
  }
  
  let output1 = identity<string>("Hello World!");
  let output2 = identity<number>(100);

console.log(output1,"output1");
console.log(output2,"output2");

以上示例identity 函数采用一个类型参数 T,以便它可以接受任意类型的参数并返回相同的类型。在调用该函数时,我们可以使用不同的类型参数来处理不同的类型。

7.1 泛型类

泛型类可以支持不特定的数据类型,要求传入的参数和返回的参数必须一致,T表示泛型,具体什么类型是调用这个方法的时候决定的

//类的泛型
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]) {
                console.log(minNum,this.list[i]);
                
                minNum = this.list[i];
            }
        }
        return minNum;
    }
}
//实例化类并且制定了类的T代表的类型是number
var m1 = new MinClas<number>();
m1.add(11);
m1.add(3);
m1.add(2);
console.log(m1.min());
//实例化类并且制定了类的T代表的类型是string
var m2 = new MinClas<string>();
m2.add('c');
m2.add('b');
m2.add('d');
console.log(m2.min());

7.2 泛型类接口

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

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

//想给ArticleCate增加数据,定义一个ArticleCate类和数据库进行映射
class ArticleCate {
    title: string | undefined;
    desc: string | undefined;
    status: number | undefined;
    constructor(params: {
        title: string | undefined,
        desc: string | undefined,
        status?: number | undefined
    }) {
        this.title = params.title;
        this.desc = params.desc;
        this.status = params.status;
    }
}

var article = new ArticleCate({
    title: "这是标题",
    desc: "这是描述",
    status: 1
});
var md2 = new MysqlDb<ArticleCate>();
md2.add(article);

8、TS修饰器

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

首先在使用修饰器前,我们得在config.js文件中打开以下两个配置 (不打开会导致无法正常执行)

8.1 类装饰器 ClassDecorator(不可传参)

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

//target是构造函数 ClassDecorator是值得类修饰器
const Base:ClassDecorator=(target)=>{
    //  console.log(target);
     target.prototype.haha="哈哈"
     target.prototype.fn=()=>{
        console.log("憨憨");
        
     }
}
@Base
class Http{
 //...
}
const http=new Http() as any
http.fn()
console.log(http.haha);

有的浏览器不支持  还可以这样写

//target是构造函数 ClassDecorator是值得类修饰器
const Base:ClassDecorator=(target)=>{
     target.prototype.haha="哈哈"
     target.prototype.fn=()=>{
        console.log("憨憨");
     }
}
@Base
class Http{
 //...
}
const http=new Http() as any
Base(Http) 
http.fn()

8.2装饰器工厂 (也可以叫柯里化,闭包,可传参)

//target是构造函数 ClassDecorator是值得类修饰器
const Base=(name:string)=>{
    const fn:ClassDecorator=(target)=>{
        target.prototype.haha=name
        target.prototype.fn=()=>{
           console.log("憨憨");
        }
    }
    return fn
    
}
@Base('小明')
class Http{
 //...
}
const http=new Http() as any
http.fn()
console.log(http.haha);

8.3 属性装饰器

属性装饰器表达式会在运行时当作函数被调用,传入下列2个参数:
1、对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
2、成员的名字。

function logProperty(params:any){
    return function(target:any,attr:any){
        target[attr] = params;
    }
}

class Person {
    @logProperty('季哈哈')
    name:string | undefined;
    constructor(){

    }
    getData(){
        console.log(this.name)
    }
}

var p = new Person();
p.getData();

8.3 方法装饰器 MethodDecorator

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

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

//  方法装饰器  一
 function logMethods(params:any){
     return function(target:any,methodsName:any,desc:any){
         console.log(target)
         //{getData: ƒ, constructor: ƒ}
         console.log(methodsName)
         //getData
         console.log(desc)
         //{writable: true, enumerable: true, configurable: true, value: ƒ}


         //扩展属性
         target.apiUrl = 'xxx';
         target.run = function():void{
             console.log('run')
         }
     }
 }


 class Person {
    name:string | undefined;

    constructor(){

    }

    @logMethods('http://www.baidu.com')
    getData(){
        console.log(this.name)
    }
}


var p:any = new Person();
console.log(p.run());




//方法装饰器  二  修改方法
function logMethods(params:any){
    return function(target:any,methodsName:any,desc:any){
        console.log(target)
        //{getData: ƒ, constructor: ƒ}
        console.log(methodsName)
        //getData
        console.log(desc)
        //{writable: true, enumerable: true, configurable: true, value: ƒ}

        console.log(desc.value)

        //修改装饰器的方法:把装饰器方法传入的所有参数修改为string类型

        //1.保存当前的方法
        var oMethod = desc.value;

        //方法被替换了
        // desc.value = function(...args:any[]){
        //     args = args.map((item) => {
        //         return String(item)
        //     })

        //     console.log(args)
        // }

        //怎么修改当前方法呢?
        desc.value = function(...args:any[]){
            args = args.map((item) => {
                return String(item)
            })

            console.log(args)
            oMethod.apply(this.args);
        }
    }
}


class Person {
   name:string | undefined;

   constructor(){

   }

   @logMethods('http://www.baidu.com')
   getData(...args:any[]){
        console.log('我是原本的返回值')
   }
}


var p = new Person();
p.getData(123,123,123,123,1231);

8.4 各种装饰器的执行顺序

/**
 * 装饰器执行顺序
 * 结果:属性装饰器  >> 方法装饰器  >>  类装饰器
 * 同样的装饰器会先执行后面的装饰器
 */

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


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

function logMethods(params:any){
    return function(target:any,methodsName:any,desc:any){
       console.log('方法装饰器')
    }
}



@logClass('类装饰器')
class Person {
    @logAttr('属性装饰器')
    name:string | undefined;
 
    constructor(){
 
    }
 
    @logMethods('方法装饰器')
    getData(){
         console.log('我是原本的返回值')
    }
 }



风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。