您现在的位置是:首页 >学无止境 >TypeScript自学文档网站首页学无止境
TypeScript自学文档
目录
3.1 基础数据类型number、string、boolean
3.18 类型过滤(Type Narrowing)集中数据筛选方式 感兴趣可以看下
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的数据类型
类型 | 例子 | 描述 |
---|---|---|
number | 1,-33,2.5 | 任意数字 |
string | “hi”,“hi”,hi | 任意字符串 |
boolean | true,false | 布尔值true或false |
字面量 | 其本身 | 限制变量的值就是该字面量的值 |
any | * | 任意类型 |
unknown | * | 类型安全的any |
void | 空值(undefined) | 没有值(或undefined) |
never | 没有值 | 不能是任何值 |
object | {name:‘孙悟空’} | 任意的JS对象 |
array | [1,2,3] | 任意JS数组 |
tuple | [4,5] | 元素,TS新增类型,固定长度数组 |
enum | enum{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
, 2
和 3
。枚举项的名称可以通过 .
访问,也可以通过枚举值反向查找到对应的名称。
需要注意的是,如果不显式为枚举项指定数值,则它们将会从 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('我是原本的返回值')
}
}