一、typeScript介绍
TypeScript是一种由微软开发的开源编程语言,它是JavaScript的一个超集。TypeScript添加了静态类型、类、接口和模块等概念,这些概念使得开发大型应用程序更加容易。TypeScript代码可以被编译成纯JavaScript代码,从而可以在任何支持JavaScript的环境中运行。
JavaScript 与 TypeScript 的区别:
TS是静态类型 JS是动态类型
- 静态类型:编写时要注意变量、函数的数据类型,并且在编译时就对代码类型检测并立即报错。
- 动态类型:编写时不需要注意变量、函数的数据类型,只有程序运行后才对代码类型检测并报错。
- 总体来说,静态类型语言更适合大型项目,可以减少错误并提高代码质量;而动态类型语言则更适合小型或快速开发项目,可以增加开发效率。
TS是强类型语言 JS是弱类型语言
- 弱类型:编程时变量可以不声明数据类型,并在赋值或运算时数据类型可以随意转换。
- 强类型:编程时必须明确声明数据类型,如果发现类型错误,就会在编译或运行期间发生错误。
二、typeScript编译器
1.安装
①使用国内镜像
npm config set registry https://registry.npmmirror.com
②安装 typescript
npm install -g typescript
③查看版本
tsc -v
2.TS自动编译和编译选项设置
- 局部-自动编译指定文件
tsc helloTS1.ts -w //对指定文件编译
-
全局-自动编译文件
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,
},
}
三、TypeScript 基础类型
数据类型 | 关键字 | 描述 |
数字类型 | number | 任意数字 例如: 0b1010(二进制)、0o744(八进制)、6(十进制)、0xf00d(十六进制)、-33、2.5 |
字符串类型 | string | 任意字符串 例如:"Runoob"、 `您好`、'2' |
布尔类型 | boolean | 布尔值true或false 例如:true,false |
任意类型 | any | 任意类型 |
未知类型 | unknown | 类型安全的any |
没有值 | void | 用于标识方法返回值的类型,表示该方法没有返回值。
|
空值 | null | 表示对象值缺失。 |
未定义值 | undefined | 用于初始化变量为一个未定义的值 |
无法达到值 | never | never 是其它类型(包括 null 和 undefined)的子类型,代表从不会出现的值。 |
数组类型 | 无 | 声明变量为数组。
|
元组 | 无 | 元组类型用来表示已知元素数量和类型的数组,各元素的类型不必相同,对应位置的类型需要相同。
|
枚举 | enum | 枚举类型用于定义数值集合。
|
注意:TypeScript 和 JavaScript 没有整数类型。
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 any
(任意类型)
any
类型表示任意类型,它可以在编译时不进行类型检查和类型推断,允许我们随意地对其进行读写操作。使用any
类型通常是为了处理一些动态类型、非常规类型或者无法确定类型的值。
let d: any;
d = 123; // 赋值为数字
d = "hello"; // 赋值为字符串
d = true; // 赋值为布尔值
console.log(d, "任意类型");
3.3 unknown
(类型未知)
概念:一个安全的any
,一个类型为unknown
的变量,不能直接赋值给其他变量
注意:unknown
数据类型的数据要赋值的话,得使用类型断言
3.4 void
(没有值)
void
用来表示空,用作函数的返回值类型,表示函数没有返回值。除了将void
类型作为函数返回值类型外,在其他地方使用void
类型是无意义的
const fn = (name: string): void => {
console.log("Hello, " + name);
}
3.5 null、undefined
可以使用 null
和 undefined
来表示可能空值或缺失的值。
let x: number | null | undefined;
x = 10; // 正确赋值
x = null; // 正确赋值
x = undefined; // 正确赋值
3.6 never
never
类型可以作为函数的返回值类型,表示该函数无法返回一个值。
function fn2():never{
throw new Error('报错了!!!');
//该函数永远无法执行到末尾,返回值类型为"never"
}
3.7 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.8 Functions
(函数)
function add(x: number, y: number): number {
return x + y;
}
//调用函数
const result = add(10, 30);
console.log(result, "函数");
3.9 Object
(对象类型)
通过接口(interface
)来进行定义和约束
//定义一个person1接口
interface person1 {
name: string;
age: number;
gender: string;
}
//使用接口定义对象
const person2: person1 = {
name: "xiaohong",
age: 20,
gender: "female",
};
console.log(person2, "对象");
3.10 元组(tuple
)
概念:元组,就是固定长度的数组,即其中的元素个数是固定的。
let h :[string,number];
h = ['a',10]
3.11 枚举(Enum
)
概念:是一种常用的数据类型,用于表示固定的预定义值集合。枚举是一种比较简单、直观的数据类型,可以用于表示有限的取值范围和状态等。特别是在开发过程中定义一些固定的常量时,枚举可以起到很好的作用,并提高代码的可读性和可维护性。
//1.枚举项的名称可以通过 . 访问,也可以通过枚举值反向查找到对应的名称。
enum Direction {
Up = "UP",
Down = "DOWN",
Left = "LEFT",
Right = "RIGHT",
}
let playerDirection: Direction = Direction.Right;
console.log(playerDirection); // 输出: RIGHT
//keyof typeof使用:name是变量,断言它的类型是枚举Direction的key
let playerDirection2: Direction = Direction[name as keyof typeof Direction]
//2.如果不显式为枚举项指定数值,则它们将会从 0 开始依次递增。
//如果第一个枚举项指定了数值,则它之后的枚举项将在此基础上增加。
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'
3.12 联合类型
TypeScript
中的联合类型使用 | 符号将几种不同类型组合成一个新的类型。表示该变量、参数或属性可以是这些集合中任何一种类型之一。
语法:
Type1
|Type2
|Type2
(表示可能为其中一种类型,“或”的意思)
- 定义一个联合类型变量
let numOrStr:number|string;
numOrStr=100; //数值
numOrStr='Hello world'; //字符串
console.log(numOrStr,"联合类型");
3.13 交叉类型
交叉类型可以用来组合现有的多个类型,形成一个新的类型,这样新类型将包含所有原始类型的成员。交叉类型使用 & 运算符进行定义。
语法:
Type1
&Type2
(表示由并集组成的新类型,“并”的意思)
interface Walk {
walk(): void;
}
interface Run {
run(): void;
}
type WalkAndRun = Walk & Run;
//相当于
interface WalkAndRun {
walk(): void;
run(): void;
}
3.14 类型推断
TS
中的类型推断是指编译器根据代码上下文自动推断变量的类型。
//1.变量声明而未指定类型
let x = 10; // TypeScript 会推断 x 的类型为 number
//2.函数返回值类型推断
function add(a: number, b: number) {
return a + b; // TypeScript 会推断出 add 函数的返回类型为 number
}
//3.上下文类型推断
window.onmousedown = function(mouseEvent) {
console.log(mouseEvent.button); // TypeScript 会推断 mouseEvent 的类型为 MouseEvent
};
3.15 类型语法(Interfaces
)
在 TypeScript
中,接口(Interface
)用于定义对象的结构,它可以包含属性、方法和索引签名。接口提供了一种定义对象类型的方式,让我们能够在代码中明确指定对象应该包含哪些属性以及它们的类型。
语法:
Interface
IName
{ }
//1.基本使用
interface IPerson {
firstName:string,
lastName:string,
sayHi: ()=>string
}
var customer:IPerson = {
firstName:"Tom",
lastName:"Hanks",
sayHi: ():string =>{return "Hi there"}
}
//2.联合类型和接口
interface RunOptions {
program:string;
commandline:string[]|string|(()=>string);
}
//3.接口和数组/对象
interface namelist {
[index:number]:string //索引签名
}
var list2:namelist = ["Google","Runoob","Taobao"]
interface ages {
[index:string]:number
}
var agelist:ages={
tom:13,
lily:12,
}
agelist["runoob"] = 15
//4.接口继承
interface Person {
age:number
}
interface Musician extends Person {
instrument:string
}
//多继承
interface IParent1 {
v1:number
}
interface IParent2 {
v2:number
}
interface Child extends IParent1, IParent2 { }
var Iobj:Child = { v1:12, v2:23}
-
索引签名
索引签名允许我们定义对象中动态属性的类型。通过使用索引签名,我们可以在接口中描述对象包含的动态属性,这些属性的名称和数量是动态的,但它们的类型是固定的。索引签名使用方括号 [] 来定义,类似于数组访问操作符。
interface SomeInterface {
[key: string]: number;
}
3.16 类型别名(Type Alias
)
类型别名是用来给一个类型起一个新的名字。类型别名可以用来简化复杂类型的引用,提高代码的可读性,并且可以在多个地方重复使用。它使用 type
关键字进行定义。
语法:
type
name
=Type1
;
type Age = number;
type Person = {
name: string;
age: Age;
greet: () => void;
};
let person1: Person = {
name: "Alice",
age: 30,
greet: function() {
console.log("Hello, my name is " + this.name);
}
};
//Age 是一个类型别名,它代表了 number 类型。
//Person 是另一个类型别名,它代表了一个包含 name、age 和 greet 成员的对象类型。
3.17 类型断言(Type Assertion
)
类型断言(Type Assertion
)是 TypeScript
中的一种特性,它允许程序员告诉编译器某个值的具体类型。这在你比编译器更了解某个值的类型时非常有用。类型断言有两种形式:尖括号语法和as
语法。
- 尖括号语法
let someValue: any = "this is a string";
let strLength: number = (<string>someValue).length;
- as语法
let someValue: any = "this is a string";
let strLength: number = (someValue as string).length;
注意:实际开发过程中会碰到有些类型没办法直接断言的情况,那么我们就需要断言两次,通常会使用as any
或者as unknown
作为第一层断言(expr as any) as T
3.18 字面类型(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", "center");
3.19 不常用类型 了解即可
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.20 类型过滤(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
四、TS
函数
- 用
function
定义函数
const 函数名 = function (参数列表): 返回值类型 {
函数体 ...
[return 返回值;]
};
- 箭头函数
const 函数名=(参数列表): 返回值类型=>{
函数体 ...
[return 返回值;]
}
- 可选参数
可以使用问号
?
将函数参数标记为可选参数。这样定义的参数可以在调用函数时不传递,而在函数内部使用默认值或者处理缺失参数的情况。
//name是必选参数,age是可选参数
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));
五、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); // 输出 '汪汪!' 和 '喵喵!'
六、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.4 方法装饰器 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.5 各种装饰器的执行顺序
/**
* 装饰器执行顺序
* 结果:属性装饰器 >> 方法装饰器 >> 类装饰器
* 同样的装饰器会先执行后面的装饰器
*/
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('我是原本的返回值')
}
}