TypeScript自学文档

news2024/12/26 11:10:46

目录

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('我是原本的返回值')
    }
 }



本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/440479.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【svn】如何批量忽略文件和文件夹

目录 一、通过svn:ignore 1、文件夹空白处右键 TortoiseSVN → Properties 打开 2、New → Other 3、global-ignores属性的值&#xff0c;即需要要忽略的文件 点击OK 4、取消忽略的文件 选中 Rmove &#xff0c;提交就可以看到idea文件了 二、svn:global-ignores 1、右键…

高通开发系列 - msm-4.9中usb初始化流程和adb功能问题

By: fulinux E-mail: fulinux@sina.com Blog: https://blog.csdn.net/fulinus 喜欢的盆友欢迎点赞和订阅! 你的喜欢就是我写作的动力! 返回高通开发系列 - 总目录 目录 USB功能集android平台adb使能流程Linux系统使能流程USB时钟USB供电U盘不能正常使用分析adb设备无法识别问…

三、JS03 DOM 操作

三、DOM 操作 3.1 JavaScript DOM 操作 DOM (Document Object Model) 为文档对象模型&#xff0c;是 HTML 和 XML 文档的编程接口 DOM 提供了对整个文档的访问模型&#xff0c;将文档作为一个树形结构 树的每个节点表示了一个 HTML 标签或标签内的文本 3.1.1 DOM 操作分类 使…

SSM整合————单表操作基础版

一、创建数据库&#xff1a; 1. 创建一个 web 项目&#xff0c;并部署到 tomcat 服务器中测试项目 是否能够正常加载并访问首页。 2. 完善项目的结构并导入 SSM 相关的jar包 3.创建SSM框架对应的配置文件 springMVC配置文件&#xff1a;1.扫描controller&#xff1b;2.配置视图…

ERROR org.springframework.web.context.ContextLoader

项目启动时报错&#xff1a; ERROR org.springframework.web.context.ContextLoader - Context initialization failed java.lang.NoSuchMethodError: org.springframework.core.annotation.AnnotationUtils.clearCache() 原因分析 这个错误的原因可能是因为 Spring 的不同…

VGG网络简介

1. 背景 VGG是常见的用于大型图片识别的极深度卷积网络&#xff0c; 这里主要介绍VGG网络预测在ImageNet数据集上的训练及预测。 2. ImageNet图像数据集简介 ImageNet包含了145W张224*224像素的三通道彩色图像数据集&#xff0c;图像划分为1000个种类。其中训练集130W张&…

Contest3070 - 计科2101~2104算法设计与分析上机作业05

问题 A: 最小平均等待时间 题目描述 有n个顾客同时在等待一项服务&#xff0c;顾客i需要的服务时间为ti&#xff0c;1≤i≤n。要安排一个服务次序使得平均等待时间最小&#xff08;平均等待时间是n个顾客等待服务时间的总和除以n&#xff09;。请编写算法&#xff0c;计算最小…

大文件上传接口响应超时

背景 开发了一个内容管理发布系统&#xff0c;在后台发布内容信息时&#xff0c;上传了一个较大的视频&#xff08;较大文件≥200M&#xff09;&#xff0c;以往未上传过如此大文件&#xff0c;然后出现报错 Status Code 413 问题&处理过程 1. status code&#xff1a;4…

如何在Linux使用 chattr 命令更改文件或目录的扩展属性?

在 Linux 操作系统中&#xff0c;chattr 命令用于更改文件或目录的扩展属性&#xff0c;包括可写性、可执行性和删除性等。本文将介绍 chattr 命令的使用方法以及常见的参数。 1. chattr 命令的基本语法 chattr 命令的基本语法如下&#xff1a; chattr [选项] [文件或目录]选…

论文阅读---《人类活动识别的准周期时间序列聚类》

论文地址&#xff1a;Quasi-Periodic Time Series Clustering for Human Activity Recognition | SpringerLink 相空间知识概念&#xff1a;相空间相关概念以及轨迹生成_末世灯光的博客-CSDN博客 摘要&#xff1a; 本文利用移动加速度计对时间序列中的周期信号进行分析&…

[python][学习]turtle.circle()函数

turtle.circle()函数 定义&#xff1a;turtle.circle(radius, extentNone) 作用&#xff1a;根据半径radius绘制extent角度的弧形 参数&#xff1a; radius &#xff1a;弧形半径 当radius值为正数时&#xff0c;圆心在…

斯坦福最新AI报告发布,12张图看懂AI现状

最近&#xff0c;斯坦福人工智能研究所&#xff08;HAI&#xff09;发布了2023年AI指数报告&#xff0c;提供了AI领域当前技术成就、政策趋势、经济影响等多方面的最新情况。 今天我们将报告最重要的12个结论精简出来&#xff0c;分享给大家。 ▍AI大模型不只是大这么简单 在…

JavaEE-轻松了解网络原理之TCP协议

目录 TCP协议TCP协议数据格式TCP原理确认应答超时重传连接管理三次握手四次挥手 滑动窗口流量控制拥塞控制延迟应答捎带应答面向字节流异常问题 TCP协议 TCP&#xff0c;即Transmission Control Protocol&#xff0c;传输控制协议. TCP协议数据格式 16位源端口号与16位目的端…

【工作小札】利用动态数据源实现Sass的一种思路(内含完整代码示例)

文章目录 1 楔子2 分析3 代码实现3.1 管理库关键库表设计3.2 租户库关键库表设计3.3 新建一个SpringBootWeb项目3.4 添加maven依赖3.5 创建初始化数据库工具类3.6 创建动态数据源配置类3.7 创建登录代码3.8 创建数据源元数据服务类3.9 创建saas服务基础父类 4 示例演示4.1 下载…

MultiHeadAttention多头注意力机制的原理

MultiHeadAttention多头注意力作为Transformer的核心组件&#xff0c;其主要由多组自注意力组合构成。 1. self-Attention自注意力机制 在NLP任务中&#xff0c;自注意力能够根据上下文词来重新构建目标词的表示&#xff0c;其之所以被称之为注意力&#xff0c;在于从上下文词…

【Spring6】| Spring6集成MyBatis3.5

目录 一&#xff1a;Spring6集成MyBatis3.5 第一步&#xff1a;准备数据库表 第二步&#xff1a;IDEA中创建一个模块&#xff0c;并引入依赖 第三步&#xff1a;基于三层架构实现&#xff0c;所以提前创建好所有的包 第四步&#xff1a;编写pojo 第五步&#xff1a;编写m…

【Redis数据库】异地公网远程登录连接Redis教程

文章目录 1. Linux(centos8)安装redis数据库2. 配置redis数据库3. 内网穿透3.1 安装cpolar内网穿透3.2 创建隧道映射本地端口 4. 配置固定TCP端口地址4.1 保留一个固定tcp地址4.2 配置固定TCP地址4.3 使用固定的tcp地址连接 转发自CSDN远程穿透的文章&#xff1a;公网远程连接R…

Java阶段二Day05

Java阶段二Day05 文章目录 Java阶段二Day05截至此版本可实现的流程图为V14UserControllerClientHandlerDispatcherServletHttpServletResponseHttpServletRequest V15DispatcherServletHttpServletResponseHttpServletRequest V16HttpServletRequestHttpServletResponse 反射JA…

SpringCloud整合AOP做日志管理

目录 1、前置知识2、步骤2.1、依赖2.2、自定义注解&#xff0c;用于注解式AOP2.3、定制切面类2.4、测试 1、前置知识 切面&#xff08;Aspect&#xff09;&#xff1a;官方的抽象定义为“一个关注点的模块化&#xff0c;这个关注点可能会横切多个对象”&#xff0c;在本例中&a…

超详细Redis入门教程——Redis命令(上)

前言 本文小新为大家带来 超详细Redis入门教程——Redis命令&#xff08;上&#xff09; 相关知识&#xff0c;具体内容包括Redis 基本命令&#xff0c;Key 操作命令&#xff0c;String 型 Value 操作命令&#xff0c;Hash 型 Value 操作命令&#xff0c;List 型 Value 操作命令…