目录
前言
在前几章中,我们学习了 TypeScript 的基础知识、函数与对象类型、接口与类、泛型编程、以及模块与命名空间。在本章中,我们将深入探讨 TypeScript 中的装饰器与高级类型操控。装饰器是一种特殊的声明,它可以被附加到类、方法、访问器、属性或参数上,用来修改它们的行为。高级类型操控则包括类型别名、映射类型、条件类型和工具类型等,帮助我们更加灵活地定义和操作类型。
- TS 入门(一):TypeScript 简介与安装
- TS 入门(二):Typescript类型与类型注解
- TS 入门(三):Typescript函数与对象类型
- TS 入门(四):TypeScript 高级类型与类型操作
- TS 入门(五):TypeScript接口与类
- TS 入门(六):TypeScript泛型编程
- TS 入门(七):TypeScript模块与命名空间
回顾模块与命名空间
在上一章中,我们学习了以下内容:
- 模块:包括导入和导出、默认导出、重命名导入和导出。
- 命名空间:包括定义命名空间、嵌套命名空间。
- 动态导入与条件导入。
正文开始
,如果觉得文章对您有帮助,请帮我三连+订阅,谢谢
💖💖💖
1. 装饰器
a. 类装饰器
类装饰器应用于类构造函数,可以用来监视、修改或替换类定义。
function sealed(constructor: Function) {
Object.seal(constructor);
Object.seal(constructor.prototype);
}
@sealed
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet() {
return `Hello, ${this.greeting}`;
}
}
b. 方法装饰器
方法装饰器应用于方法,接受三个参数:类的原型、方法的名称和方法的描述符。
function enumerable(value: boolean) {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
descriptor.enumerable = value;
};
}
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
@enumerable(false)
greet() {
return `Hello, ${this.greeting}`;
}
}
c. 访问器装饰器
访问器装饰器应用于访问器(getter 或 setter),和方法装饰器一样,接受三个参数。
function configurable(value: boolean) {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
descriptor.configurable = value;
};
}
class Point {
private _x: number;
private _y: number;
constructor(x: number, y: number) {
this._x = x;
this._y = y;
}
@configurable(false)
get x() {
return this._x;
}
@configurable(false)
get y() {
return this._y;
}
}
d. 属性装饰器
属性装饰器应用于类的属性,接受两个参数:类的原型和属性的名称。
function format(formatString: string) {
return function (target: any, propertyKey: string) {
let value: string;
const getter = () => value;
const setter = (newVal: string) => {
value = `${formatString} ${newVal}`;
};
Object.defineProperty(target, propertyKey, {
get: getter,
set: setter,
enumerable: true,
configurable: true,
});
};
}
class Greeter {
@format("Hello,")
greeting: string;
constructor(message: string) {
this.greeting = message;
}
}
let greeter = new Greeter("world");
console.log(greeter.greeting); // "Hello, world"
e. 参数装饰器
参数装饰器应用于方法参数,接受三个参数:类的原型、方法的名称和参数在函数参数列表中的索引。
function logParameter(target: any, propertyKey: string, parameterIndex: number) {
const existingRequiredParameters: number[] = Reflect.getOwnMetadata("requiredParameters", target, propertyKey) || [];
existingRequiredParameters.push(parameterIndex);
Reflect.defineMetadata("requiredParameters", existingRequiredParameters, target, propertyKey);
}
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet(@logParameter name: string) {
console.log(`${this.greeting}, ${name}`);
}
}
let greeter = new Greeter("Hello");
greeter.greet("world"); // "Hello, world"
2. 高级类型操控
a. 类型别名
类型别名用于为类型创建新名称。
type Name = string;
type NameResolver = () => string;
type NameOrResolver = Name | NameResolver;
function getName(n: NameOrResolver): Name {
if (typeof n === "string") {
return n;
} else {
return n();
}
}
let name: Name = "Alice";
let nameResolver: NameResolver = () => "Bob";
console.log(getName(name)); // "Alice"
console.log(getName(nameResolver)); // "Bob"
b. 映射类型
映射类型用于根据旧类型创建新类型。
interface Person {
name: string;
age: number;
}
type ReadonlyPerson = Readonly<Person>;
let readonlyPerson: ReadonlyPerson = { name: "Alice", age: 30 };
// readonlyPerson.age = 35; // 错误,age 是只读属性
type PartialPerson = Partial<Person>;
let partialPerson: PartialPerson = { name: "Alice" }; // 允许缺少 age 属性
c. 条件类型
条件类型根据条件选择类型。
type MessageOf<T> = T extends { message: unknown } ? T["message"] : never;
interface Email {
message: string;
}
interface Dog {
bark(): void;
}
type EmailMessageContents = MessageOf<Email>; // string
type DogMessageContents = MessageOf<Dog>; // never
d. 工具类型
TypeScript 提供了一些内置的工具类型,帮助我们快速操作类型。
interface Person {
name: string;
age: number;
location?: string;
}
type PartialPerson = Partial<Person>; // 所有属性变为可选
type ReadonlyPerson = Readonly<Person>; // 所有属性变为只读
type PickNameAndAge = Pick<Person, "name" | "age">; // 只保留 name 和 age 属性
type OmitLocation = Omit<Person, "location">; // 去除 location 属性
3. 实用的装饰器和类型操控
a. 日志装饰器
日志装饰器可以用于记录方法的调用。
function logMethod(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
console.log(`Calling ${propertyKey} with arguments: ${JSON.stringify(args)}`);
const result = originalMethod.apply(this, args);
console.log(`Method ${propertyKey} returned: ${JSON.stringify(result)}`);
return result;
};
return descriptor;
}
class Calculator {
@logMethod
add(a: number, b: number): number {
return a + b;
}
}
let calculator = new Calculator();
calculator.add(2, 3); // 输出日志信息
b. 自定义工具类型
你可以创建自定义工具类型,以满足特定需求。
type Nullable<T> = { [P in keyof T]: T[P] | null };
interface Person {
name: string;
age: number;
}
type NullablePerson = Nullable<Person>;
let person: NullablePerson = { name: null, age: null }; // 所有属性可以为 null
结语
通过本章的学习,你应该对 TypeScript 中的装饰器与高级类型操控有了更深入的理解。掌握这些内容将使你能够编写更加灵活、可扩展和可维护的代码。在下一章中,我们将探讨 TypeScript 中的类型声明文件与异步编程,包括类型声明文件、Promise 类型、async/await 和异步迭代器等内容。务必掌握好每个概念,它们将为你后续学习 TypeScript 提供坚实的基础。