TS 入门(八):TypeScript装饰器与高级类型操控

前言

在前几章中,我们学习了 TypeScript 的基础知识、函数与对象类型、接口与类、泛型编程、以及模块与命名空间。在本章中,我们将深入探讨 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 提供坚实的基础。

  • 15
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ziyu_jia

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值