TypeScript基础
ts中的基础类型
ts
/**
* ts 类型:
* 1. 内置类型 (DOM Promise 原始方法)
* 2. 基础类型
* 3. 高级类型
* 4. 自定义类型
*/
/**
* ts 中 变量: 类型 = 值
*/
let name: string = "alydia";
let age: number = 30;
// 元组 规定长度和存储的类型
let tuple1: [string, number, boolean] = ["a", 30, true];
// 添加只能添加元组中已经存在的类型
tuple1.push("abc"); // 为了安全, 因为不确定这个值是否存在
tuple1[0];
// 枚举类型,自带类型的对象
enum USER_ROLE {
USER,
ADMIN,
MANAGER,
}
// null 和 undefined
// 任何类型的子类型
let str: string ;
// void 代表函数的返回值为空, 只在函数中使用
// never 类型
// 任何类型的子类型
function fn1(): never {
throw new Error();
}
// 类型保护,保障程序的不缺失 完整性保护
// 针对不同的类型做不同处理
function validate(val: never) {}
function getResult(strOrNumORBool: string | number | boolean) {
if (typeof strOrNumORBool === "string") {
return strOrNumORBool.toUpperCase();
}
if (typeof strOrNumORBool === "number") {
return strOrNumORBool.toFixed(2);
}
if (typeof strOrNumORBool === "boolean") {
return strOrNumORBool ? "是的" : "不是";
}
validate(strOrNumORBool);
}
// object 对象类型
// object, {}, Object
// object 是一个更小的范围, {} 是最大的范围
let obj: {} = "123"; // 最大范围
// Symbol BigInt es6新增的
let s1: symbol = Symbol("s1");
let s2: symbol = Symbol("s2");
// string number boolean null undefined array tuple never object symbol bigint any void
export {};
联合类型断言
ts
// 以赋予值得结果 来推导内容
// let name = "John";
// let age = 30;
// let const 区别
// const age = 30; 如果用常量 来自动推导类型就是字面类型
let name: string | number;
// 默认没有赋值得时候,联合类型可以调用公共得方法,为了安全所有只能访问公共得属性
name = "John"; // 字符串类型
name.toUpperCase();
name = 40;
name.toFixed(); // 赋值后会推断类型
// 字面量类型
type Direction = "up" | "down" | "left" | "right";
let direction: Direction;
direction = "up"; // 只能是这四个值之一
// type 中定义的是类型 不是js 中对象
type women = {
wealthy: true;
age: number;
name: string;
};
// 断言 (非空断言, 这个值一定不为空, 绕过 ts 检查)
let ele = document.getElementById("app");
ele!.style.color = "red"; // 可能会报错,因为ele可能为null
// as 断言 可以强制把某个类型断言成已经存在的某个类型
let ele2: HTMLElement | null = document.getElementById("app") as HTMLElement;
ele2.style.color = "red"; // 可能会报错,因为ele可能为null
ele?.style.color; // 可选链,如果ele为null则不执行后面的代码
export {};
函数
ts
// 函数类型
// 函数 function 关键字来定义函数
// 表达式定义 (可以描述变量的类型)
// 函数有入参 和 返回值 (针对这个部分,掌握类型)
// 函数本身的类型
// type ISum = (x: number, y: number) => number;
// let sum2: ISum = (x, y) => {
// return x + y;
// }
// sum2(10, 20);
// 1) 常见的类型推导的方式
let name = "1";
let age = 10;
// 2) 根据返回值来进行类型推导, 自动推导返回值类型
// function sum(x: number, y: number): number {
// return x + y;
// }
type ISum = (x: number, y: number) => number;
let sum: ISum = (x, y) => {
return x + y;
};
// 3) 会根据上下文来推导赋予值的类型
type ICallback = (x: number, y: number) => void;
// void 表示不关心返回的具体类型
function test(callback: ICallback) {}
test((x, y) => {
console.log(x + y);
});
// 4) 函数中的可选参数
let sum3: (x: number, y?: number) => number = (x, y = 0) => {
return x + y;
};
// 5) 函数中的剩余参数
let sum4: (x: number, ...args: number[]) => number = (x, ...args) => {
return args.reduce((total, current) => total + current, x);
};
// 6) 可以采用 ts 中的 typeof 来获取变量的类型
let personalbar = {
name: "个人工具栏",
age: 10,
getName() {
return this.name;
},
};
function getVal(this: typeof personalbar, key: keyof typeof personalbar) {
return this[key];
}
let r = getVal.call(personalbar, "name"); // 个人工具栏
// 7) 函数的重载 ts 中的函数重载是伪重载(类型的重载 而不是逻辑的重载)
function toArray(value: number): number[];
function toArray(value: string): string[];
function toArray(value: number | string) {
if (typeof value === "number") {
return value.toString().split("").map(Number);
} else {
return value.split("");
}
}
let arr = toArray(123); // [1, 2, 3]
let arr2 = toArray("123"); // ['1', '2', '3']
export {};
类中类型
ts
// 类本身就可以是一个类型,可以描述实例
// ts 中要求所有的属性 必须先声明再使用
class Circle {
public x!: number;
public y!: number;
constructor(x: number, y: number) {
this.x = x;
this.y = y || 100;
}
// ...
}
// public 公开属性
// private 私有属性,只能在类的内部访问
// protected 受保护属性,只能在类的内部和子类中访问
// readonly 只读属性,只能在类的内部访问,不能修改
// static 静态属性,可以通过类名直接访问,而不需要实例化
// #abc 私有属性,只能在类的内部访问
class Animal {
#abc = 123; // 私有属性,只能在类的内部访问
constructor(public name: string, public age: number) {}
}
class Cat extends Animal {
constructor(name: string, age: number, public color: string) {
super(name, age);
}
}
const animal = new Animal("Dog", 5);
console.log(animal);
const cat = new Cat("Cat", 3, "Black");
console.log(cat);
接口的使用
ts
/**
* 1. 接口不能具有具体的实现, 可以用于描述 函数 混合类型 对象 类
*/
// type IFullname = {
// firstName: string;
// lastName: string;
// };
interface IFullname {
firstName: string;
lastName: string;
}
type IFn = { (obj: IFullname): string };
const fullname: IFn = ({ firstName, lastName }) => {
return `${firstName} ${lastName}`;
};
/**
* type 和 interface 的区别:
* 1. type 可以定义基本类型, 联合类型, 元组类型等,
* 2. interface 只能定义对象类型
* 3. 如果只是用来描述结构用 interface
* 4. 如果需要定义基本类型, 联合类型, 元组类型等用 type
* 5. interface 可以被继承, type 不能被继承
* 6. type 不能重名, interface 可以合并
* 7. type 在后续的学习中可以使用循环和条件, interface 不行
* 其他情况下可以呼唤 (函数类型一般用 type 来声明)
*/
fullname({
firstName: "John",
lastName: "Doe",
});
type Ifn = {
(): number;
count: number;
};
const click: Ifn = () => {
return click.count++;
};
// 为了防止这个 click 函数被重新赋值, let 是可以被修改的, 如果用 const 就不一样了
click.count = 0;
/**
* 1. 如果对象中的属性 多于接口可以直接采用断言的方式来赋值
* 2. 可以基于接口的特性 扩展类型 写一个同名接口
* 3. 产生一个新类型
* 4. 类型兼容
* 5. 交叉类型&
* 6. 任意类型
*/
interface IVeg {
color: string;
size: number;
[key: string]: any; // 允许有其他属性
}
const carrot: IVeg = {
color: "orange",
size: 5,
a: 1,
}; // 断言, 只要有 color 和 size 就可以了
泛型
ts
class Animal {
constructor(public name: string, public age: number) {}
}
class Person {
constructor(public name: string, public age: number) {}
}
function createInstance(target: typeof Animal, name: string, age: number) {
return new target(name, age);
}
const dog = createInstance(Person, "Dog", 5);
// 泛型使用的时候传递类型,可以直接推导,但是内部调用的时候没有确定类型
type ICallback<T> = (name: T, age: number) => void;
type IforEach = <T>(arr: T[], callback: ICallback<T>) => void;
const forEach: IforEach = (arr, callback) => {
for (let i = 0; i < arr.length; i++) {
callback(arr[i], i);
}
};
forEach([1, 2, "3"], (item, index) => {
console.log(`Item: ${item}, Index: ${index}`);
});
// 写在前面 就是表示使用类型的时候传参,写后面 意味着调用函数的时候传递类型
// 泛型是有默认值的
// 使用一些联合类型的时候,会使用泛型
type Union<T = boolean> = T | number | string;
let union: Union<boolean> & {}= true; // 默认值为 boolean
// 泛型约束
type IKeyValue<T extends string | number, U> = {
key: T;
value: U;
};
interface IWithLen {
length: number;
}
// extends指只要泛型中有 length 属性即可
function handle2<T extends IWithLen>(arr: T) {
return arr.length;
}
handle2({ a: 1, b: 2, length: 3 });
function getVal<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
getVal({ name: "Alice", age: 30 }, "name"); // 返回类型为 string
interface IResponse<T> {
code: number;
message?: string;
data: T;
}
interface ILoginData {
token: string;
roles: number[];
}
function toLogin(): IResponse<ILoginData> {
return {
code: 200,
data: {
token: "abc123",
roles: [1, 2, 3],
},
};
}
// 获取最大值
class MyArray<T> {
private arr: T[] = [];
set(value: T): void {
this.arr.push(value);
}
getMax(): T | null {
if (this.arr.length === 0) return null;
return this.arr.reduce((max, current) => (current > max ? current : max));
}
}
let myArr = new MyArray<number>();
myArr.set(100);
myArr.set(200);
myArr.set(300);
console.log(myArr.getMax()); // 输出 300
export {};
交叉类型
ts
/**
* & 交叉类型
* | => ||
* & => &&
*
* | 并集
* & 交集 ts 中的
*/
interface Person1 {
handsome: string;
}
interface Person2 {
high: string;
}
interface Person3 {
rich: string;
}
type Person = Person1 & Person2 & Person3;
let person: Person = {
handsome: "yes",
high: "yes",
rich: "yes",
};
export {};
unknow类型
ts
// unknown 是 any 的安全类型, 泛型没有赋予值的时候 默认就是 unknown
let val: unknown = true;
// unknown 不能直接赋值给其他类型
// let val1: string = val; // error
function processInput(val: unknown) {
if (typeof val === "number") {
val.toFixed(2); // 可以调用 number 的方法
} else if (typeof val === "string") {
val.toUpperCase(); // 可以调用 string 的方法
}
}
let val1: unknown = "hello";
(val1 as string).toUpperCase(); // ok, 通过类型断言将 unknown 转为 string
type IKey = keyof any
条件类型
ts
// 条件类型
// 和泛型约束通常一起使用, 类似三元运算符, 泛型约束是用来约束泛型的(也包含了判断), 条件是用来判断的
type ResStatusMessage<T extends number> = T extends 200 | 204 | 206
? "success"
: "fail";
type R1 = ResStatusMessage<200>; // "success"
type R2 = ResStatusMessage<404>; // "fail"
type Conditional<T, U> = T extends U ? "yes" : "no";
type R3 = Conditional<"hello", string>; // "yes"
type R4 = Conditional<"hello", number>; // "no"
interface Bird {
name: "鸟";
}
type FormatReturnVal<T extends string | number> = T extends string
? string
: T extends number
? number
: never;
function sum<T extends string | number>(a: T, b: T): FormatReturnVal<T> {
// 泛型类型不能作 数学运算
return a + (b as any);
}
let result = sum("hello", "world"); // "helloworld"
let result2 = sum(1, 2); // 3
类型兼容
ts
type R1 = "abc" extends string ? true : false; // true
type R2 = 123 extends number ? true : false; // true
type R3 = true extends boolean ? true : false; // true
let r1: string = "abc";
let r2: number = 123;
let r3: boolean = true;
// 字面量类型可以赋予给基础类型
type R4 = "a" extends "a" | "b" | "c" ? true : false; // true
type R5 = 1 extends 1 | 2 | 3 ? true : false; // true
type R6 = true extends true | false ? true : false; // true
// 字面量类型可以赋予给字面量的联合类型
let r4: "a" | "b" | "c" = "a";
// 类型层面上的, 低类型可以赋予高类型
// 从结构上考虑的 交叉类型 可以赋予交叉前的类型
type R7 ={} extends object ? true : false; // true
type R8 ={} extends Object ? true : false; // true
type R9 = object extends {} ? true : false; // true
type R10 = Object extends {} ? true : false; // true
export {};
内置类型
ts
// 正常判断类型的时候 可以通过 A extends B A 是 B 的子类型
// 条件分发
// 1.A类型是通过泛型传入的
// 2.A类型如果是联合类型会进行分发
// 3.泛型参数A 必须是完全裸露的 才具备分发的能力
interface Bird {
name: "鸟";
}
interface Sky {
name: "天";
}
interface Fish {
name: "鱼";
}
interface Water {
name: "水";
}
type Conditional = Fish | Bird extends Fish ? Water : Sky;
type Conditional2<T> = T extends Fish ? Water : Sky;
type R1 = Conditional2<Bird | Fish>; // 将联合类型的每一项单独的进行比较
// 默认情况下 有些时候我们需要关闭这种分发能力, 会造成判断不准确
type Conditional3<T, U> = T extends U ? true : false;
type R2 = Conditional3<1 | 2, 1>;
// 禁用分发
type NoDistribute<T> = T & {};
type Conditional4<T, U> = NoDistribute<T> extends U ? true : false;
type R3 = Conditional4<1 | 2, 1>;
type Conditional5<T, U> = [T] extends [U] ? true : false;
type R4 = Conditional5<1 | 2, 1>;
// 条件判断还有一些注意项
type IsNever<T> = [T] extends [never] ? true : false;
// never在直接比较的时候无法返回正确的结果
type R5 = IsNever<never>; // never
// 内置1: Extract
// type MyExtract<T, U> = T extends U ? T : never;
type R6 = Extract<1 | 2 | 3, 1 | 2>; // 求交集
// type MyExclude<T, U> = T extends U ? never : T;
type R7 = Exclude<1 | 2 | 3 | 4 | 5, 2 | 4>; // 1 | 3 | 5
// type MyNonNullable<T> = T & {};
type R8 = NonNullable<1 | 2 | null | undefined>; // 1 | 2
// 可以求联合类型的交集和差集 Extract 和 Exclude 后续可以求对象的属性的交集和差集
// infer 类型判断
// infer 可以在条件类型中提取类型的某一个部分,
// 在使用的时候想获取什么类型就将他写在什么"地方"加一个变量可以自动的来推导
// 类型推导都是基于位置的
// 1.获取函数的返回值类型
function getObject(name: string, age: number, sex: string) {
return { name, age };
}
// type ReturnType<T extends (...args: any[]) => any> = T extends (
// ...args: any[]
// ) => infer R
// ? R
// : never;
// //泛型约束的目的是限制泛型传入的,后面的条件是逻辑
// type R9 = ReturnType<typeof getObject>; // 使用 infer 需要先创造一个条件才可以
type Parameters<T extends (...args: any[]) => any> = T extends (
...args: infer P
) => any
? P
: never;
//泛型约束的目的是限制泛型传入的,后面的条件是逻辑
type R10 = Parameters<typeof getObject>; // 使用 infer 需要先创造一个条件才可以
class A {
constructor(name: string) {}
}
type ConstructorParameters<T extends new (...args: any[]) => any> =
T extends abstract new (...args: infer P) => any ? P : never;
type R11 = ConstructorParameters<typeof A>; // 取类本身的类型判断构造函数的参数
type InstanceType<T extends new (...args: any[]) => any> =
T extends abstract new (...args: any[]) => infer P ? P : never;
type R12 = InstanceType<typeof A>; // 取类本身的类型判断构造函数的参数
function createInstance<T extends new (...args: any[]) => any>(
target: T,
...args: ConstructorParameters<T>
) {
return new target(...args);
}
class MyPerson {
constructor(public name: string, public age: number) {}
}
createInstance(MyPerson, "zhangsan", 18);
// 2.操作
// swap
type Swap<T> = T extends [infer A1, infer A2] ? [A2, A1] : never;
type R13 = Swap<["zs", 30]>; // 30, 'zs'
type SwapHeadTail<T> = T extends [infer H, ...infer N, infer T]
? [T, ...N, H]
: never;
type R14 = SwapHeadTail<[1, 2, 3, 4, 5, 6, 7]>;
// promise 如果返回的是一个promise 会不停的解析这个promise
function getVal(): Promise<100> {
return new Promise((reslove) => {
reslove(100);
});
}
type PromiseReturnValue<T> = T extends Promise<infer P>
? PromiseReturnValue<P>
: T;
type R15 = PromiseReturnValue<Promise<Promise<Promise<100>>>>;
// 通过 infer 来实现递归推导
// 将元组转化成联合类型 [number, boolean, string] => number | boolean | string
type ElementToUnion<T> = T extends Array<infer E> ? E : never;
type TupleToArray = ElementToUnion<[number, string, boolean]>;
// 3.重构类型的结构 T & K
// Partial
interface Person {
name: string;
age: number;
adddress: { n: string };
}
type Partial<T> = { [K in keyof T]?: T[K] };
type DeepPartial<T> = {
[K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K];
};
type PartialPerson = Partial<Person>;
let person: PartialPerson = { name: "zs" };
// Required
type Required<T> = {
[K in keyof T]-?: T[K];
};
let person2: Required<PartialPerson> = {
name: "zs",
age: 30,
adddress: { n: "1" },
};
// ReadOnly & Mutate
type ReadOnly<T> = {
readonly [K in keyof T]: T[K];
};
type Mutate<T> = {
-readonly [K in keyof T]: T[K];
};
let person3: Mutate<ReadOnly<PartialPerson>> = {
name: "zs",
age: 30,
};
// Pick Omit 重构对象的结构 可以采用这两个类型
type Pick<T, K extends keyof T> = {
[Key in K]: T[Key];
};
type PickPerson = Pick<Person, "name" | "age">;
// 在很多属性中挑选需要的,在很多属性中排除不需要的
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
type OmitPerson = Omit<Person, "name" | "age">;
// 实现 minxin
// 针对这种情况,应该将B 有的属性 在A中移除
function mixin<T, K>(a: T, b: K): Omit<T, keyof K> & K {
return { ...a, ...b };
}
let x = mixin({ name: "zs", age: 18 }, { name: 123, age: "22", c: 2 });
type Computed<T> = {
[K in keyof T]: T[K];
};
type nameType = Computed<typeof x>;
// keyof 取key
// typeof 取类型的
// 索引查询 []
// in 循环
// extends 条件
// Record
let person6: Record<string, any> = { 1: "abc" };
function map<T extends keyof any, K>(
obj: Record<T, K>,
callback: (value: K, key: T) => any
) {
let r = {} as any;
for (let key in obj) {
r[key] = callback(obj[key], key);
}
return r;
}
let mapResult = map({ name: "zs", age: 30 }, (value, key) => {
return "abc";
});
export {};
类型兼容性
ts
// 鸭子类型检测 结构化类型检测
// 子类型可以赋予给父类型, 从结构角度触发. ts比较的不是类型的名称,而是这个结构上的属性和方法
// 1)基础类型的兼容性问题
// 将一个值赋予给另一个值可以产生兼容性
// 基础类型的兼容性问题
let obj: {
toString(): string;
};
let str: string = "";
obj = str;
// 从安全角度出发,你要的属性我都满足,只能访问已经存在的属性,不存在的无法访问
// 2)接口的兼容性
interface IPerson {
name: string;
age: string;
}
interface IAnimal {
name: string;
age: string;
address: string;
}
let person!: IPerson;
let animal!: IAnimal;
// person = animal; // success
// animal = person; // fail
// 在后台返回的数据中我们可以预先定义好接口类型. 多的属性也可以赋值给这个类型
// 3)函数的兼容性
let s1 = (a: string, b: string) => a + b;
let s2 = (a: string, b: string, c: string) => a + b + c; // ts 基于位置来推导的
// s1 = s2; // fail
s2 = s1; // success
// 参数个数只能少,不能多
function forEach<T>(
arr: T[],
callback: (item: T, index: number, array: T[]) => void
) {
for (let i = 0; i < arr.length; i++) {
callback(arr[i], i, arr);
}
}
forEach([1, 2, 3], (item, index, array) => {});
// 函数的逆变与协变 函数的参数是逆变 返回值是协变
class Parent {
home() {}
}
class Child extends Parent {
car() {}
}
class Grandson extends Child {
money() {}
}
// 函数的参数是逆变的
// let t1: (instance: Child) => void = (instance: Parent) => ""; // success
// let t2: (instance: Child) => void = (instance: Grandson) => ""; // fail
// 函数的返回值是协变的
// let t3: (instance: Child) => void = (instance: Child) => new Grandson(); // success
// let t4: (instance: Child) => void = (instance: Child) => new Parent(); // success
// 传递的函数 (传父 (参数是逆变的) 返子 (返回值是协变的))
// 对于函数的兼容性而言, 参数个数更少, 传递的可以是父类, 返回值可以返回儿子
// 推导公式:
type Arg<T> = (arg: T) => void;
type Return<T> = (arg: any) => T;
type ArgType = Arg<Parent> extends Arg<Child> ? true : false; // 逆变
type ReturnType = Return<Grandson> extends Return<Child> ? true : false; // 协变
interface MyArray<T> {
concat(...args: T[]): T[];
// concat: (...args: T[]) => T[]; // 这中方式会检测逆变
}
let arr1!: MyArray<Parent>; // [Parent]
let arr2!: MyArray<Child>; // [Child]
// arr1 -> (...args: Parent[]): Parent[];
// arr2 -> (...args: Child[]): Child[];
arr1 = arr2;
// 4) 泛型的兼容性
// ts 比较的是结构, 结构一致即可
interface TT<T> {}
let o1!: TT<string>;
let o2!: TT<number>;
o2 = o1;
// 5) 枚举不具备兼容性问题
enum E1 {}
enum E2 {}
let e1!: E1;
let e2!: E2;
// e2 = e1; // error
// 6) 类的兼容性
class A {
public name!: string;
}
class B {
public name!: string;
age!: string;
}
let b: A = new B(); // 比较的是属性 不符合就不兼容, 如果类中存在私有属性或者收保护的属性, 则不能兼容
// ts 比较类型结构的时候比较的是属性和方法
// 如果属性和放到都满足则兼容, 有一些比较特殊
// 基础类型和对象类型的兼容, 接口的兼容, 泛型的兼容, 枚举的兼容, 类的兼容
// 在其他语言中存在标称类型 (根据名称开区分类型)
type Nominal<T, K extends string> = T & { __tag: K };
type BTC = Nominal<number, "btc">;
type USDT = Nominal<number, "usdt">;
let btc: BTC = 1000 as BTC;
let usdt: USDT = 1000 as USDT;
function getVal(val: BTC) {
return val;
}
getVal(btc);
export {};
装饰器
ts
/**
* 装饰器就是一个函数, 只能在类中使用 (类本身, 类成员使用)
* 装饰器的分类
* 1. 类的装饰器
* 2. 方法装饰器
* 3. 属性装饰器
* 4. 访问装饰器
* 5. 参数装饰器
* ts.config.json "experimentalDecorators": true,
*/
// 1) 类的装饰器 给类来进行扩展的. 也可以返回一个子类去返回一个父类
const classDecorator = <T extends new (...args: any[]) => any>(target: T) => {
(target as any).type = "动物";
(target as any).getType = function () {
return this.type;
};
Object.assign(target.prototype, {
eat() {},
drink() {},
});
};
function OverrideAnimal(target: any) {
return class extends target {
eat() {
super.eat();
console.log("new eat");
}
};
}
// 2) 方法装饰器
function Enum(isEnum: boolean): MethodDecorator {
return function (target, propertyKey, descriptor) {
// desriptor.enumable // 是否可枚举
// desriptor.writable // 是否能被重写
// desriptor.configurable // 是否属性能被删除
// desriptor.value // 当前函数的值
descriptor.enumerable = isEnum;
let original = descriptor.value as any;
descriptor.value = function () {
console.log("prev eat");
return original(...arguments);
} as typeof descriptor.value;
};
}
// 3) 属性装饰器
function ToUpper(isUpper: boolean): PropertyDecorator {
return function (target, propertyKey) {
let val = "";
Object.defineProperty(target, propertyKey, {
get() {
return val.toUpperCase();
},
set(newVal) {
val = newVal;
},
});
};
}
// 可以描述属性装饰器 get 和 set
function valToUpper(target: any, key: string, desciptor: any) {
let originalSet = desciptor.set;
desciptor.set = function (newVal: string) {
return originalSet.call(this, newVal.toUpperCase());
};
}
class Animal {
@ToUpper(true)
public name: string = "animal";
@Enum(true) // 最终装饰器必须返回一个函数
eat() {
console.log("动物 original");
}
private _val!: string;
@valToUpper
get val() {
return this._val;
}
set val(newVal) {
this._val = newVal;
}
}
const animal = new Animal();
animal.val = "abc";
console.log(animal.val); // "ABC"
export {};
装饰器执行顺序
ts
function Echo(val: string) {
return function (target: object, key?: string, desciptor?: any) {
console.log(val, target, key, desciptor);
};
}
@Echo("装饰器2")
@Echo("装饰器1")
class Flow {
constructor(@Echo("构造函数的参数装饰器") str: any) {}
@Echo("原型方法")
handler(@Echo("原型方法的参数") str: any) {}
@Echo("静态属性")
static type = "xxx";
@Echo("静态方法")
static getType() {
return this.type;
}
@Echo("实例属性")
name!: string;
@Echo("属性访问器")
get value() {
return "aaa";
}
}
/**
* 执行顺序
* 1. [实例属性 方法 属性访问]
* 2. [静态属性 静态方法]
* 3. [类的装饰器]
* 一个函数 对原来的内容不停地包裹 (洋葱模型)
*/
// 装饰器一般会搭配反射来使用
// 元数据? 描述数据的数据
export {};