借助 Trae Builder 把 TypeScript 的碎片化学习记录整理成文档

TypeScript 学习记录

1. TypeScript 简介

TypeScript 是 JavaScript 的超集,它添加了静态类型系统和面向对象编程的特性,使代码更加健壮和可维护。

补充说明:

  • 本文为个人的 TypeScript 学习笔记,内容基于 B 站课程视频学习,并结合豆包、扣子等ai工具查阅知识点、核对细节,融入个人思考与总结,最终通过 Trae Builder 结构化整理、多次校验后生成文档,用于个人复习与查阅参考。
  • 如今学习编程的资源非常丰富,文档、视频、AI 工具都能高效辅助,大幅节省自己查阅知识点的时间,也省去注册各种知识网站账号的麻烦,特别是自学时有疑问问题或者突发奇想时,都可用ai工具回答或论证。过去我习惯零散记录、收藏他人资料,复习时难以串联知识点、难以共鸣自己的学习经历;现在借助 AI 辅助整理与校验,既能把学习过程梳理成线性、完整、严谨的笔记,又能确保内容准确、表述规范,解决了自己懒得整理的遗憾。

主要特性

  • 静态类型检查
  • 面向对象编程支持
  • 类型推断
  • 接口
  • 泛型
  • 装饰器
  • 模块系统

安装与使用

使用 npm 安装
bash 复制代码
# 全局安装 TypeScript
npm install -g typescript
# 或当前项目安装(仅练习时更推荐)
npm install typescript --save-dev


# 验证安装成功(查看版本)
# 若全局安装
tsc -v
# 若当前项目安装,需使用 npx 执行
npx tsc -v
# 输出类似:Version 5.3.3 即为成功
编译命令
  • tsc:TypeScript Compiler 命令
  • -w--watch:自动监听文件变化并编译
bash 复制代码
# 若全局安装
tsc -w
# 若当前项目安装,需使用 npx 执行
npx tsc -w

2. 类型系统

  • TS 的核心是编译时的类型操作:通过定义类型规则,在代码运行前就拦截类型错误,本质是给 JS 加了一层 "类型校验层";

  • JS 的核心是运行时的变量操作:不做类型预设,直接处理变量的值,所有逻辑和错误都在运行时体现;

  • 两者的本质关系:TS 是 JS 的超集,TS 的类型逻辑只存在于编译阶段,最终运行的还是 JS 的变量逻辑。

2.1 基本类型

类型 描述 示例
string 字符串 let name: string = "张三"
number 数字 let age: number = 18
boolean 布尔值 let isActive: boolean = true
null 空值 let n: null = null
undefined 未定义 let u: undefined = undefined
bigint 大整数 let big: bigint = 100n
symbol 唯一标识符 let sym: symbol = Symbol("id")
object 非原始类型 let obj: object = {}

注意点:在JavaScript中的这些内置构造函数:Number、String、Boolean(包装对象),它们用于创建对应的包装对象,在日常开发中很少使用,在TypeScript中也是同理,所以在TypeScript中进行类型声明时,通常都是用小写的number、string、boolean (原始类型,注意:此处近关于这三种)

2.2 原始类型 vs 包装对象

  • 原始类型:占用空间少,处理速度快
  • 包装对象:是复杂类型,内存占用大,日常开发很少使用
  • 自动装箱:JavaScript 在必要时会自动将原始类型转换为包装对象,以便调用方法或访问属性
typescript 复制代码
// 作用:演示原始类型string与包装对象类型String的区别
let str1: string;
str1 = "hello";
// str1 = new String('hello') // 报错,不能将包装器对象赋值给原始类型

let str2: String;
str2 = "hello";
str2 = new String("hello"); // 通常情况下不会这么使用,故类型声明还是只用小写的string

console.log(typeof str1); // string
console.log(typeof str2); // object


### 2.3 特殊类型

| 类型      | 描述                       | 示例                                            |
| --------- | -------------------------- | ----------------------------------------------- |
| `any`     | 任意类型(放弃类型检查)   | `let a: any = "hello"`                          |
| `unknown` | 未知类型(类型安全的 any) | `let u: unknown = 123`                          |
| `void`    | 无返回值                   | `function fn(): void {}`                        |
| `never`   | 永远不会返回               | `function error(): never { throw new Error() }` |
| `tuple`   | 固定长度和类型的数组       | `let t: [string, number] = ["hello", 123]`      |
| `enum`    | 枚举类型                   | `enum Direction { Up, Down, Left, Right }`      |

2.4 常用类型详细解释

类型 特点描述 使用场景
any 放弃类型检查,可接受任意值,也可赋值给任意类型 临时绕过类型检查,迁移旧代码
unknown 类型安全的 any,使用前必须进行类型检查或断言 不确定数据类型,但需要安全处理
never 表示永远不会发生的值,不能赋值任何值 抛出异常的函数、无限循环、穷尽检查
void 表示无返回值,函数返回空 无返回值的函数
object 表示所有非原始类型(对象、函数、数组等) 需要存储复杂类型,但具体类型不确定
tuple 固定长度、固定类型的数组 函数返回多个值、精确描述数据结构
enum 定义一组命名常量,增强代码可读性 表示一组相关的常量值
代码示例

any 类型

  • 作用:任意类型,放弃类型检查(显式声明、隐式声明),可接受任意值,也可赋值给任意类型
typescript 复制代码
let a: any = "hello";
a = 123;        // 不报错
a = true;       // 不报错
let str: string = a;  // 不报错,但可能引发运行时错误

unknown 类型

  • 作用:未知类型,类型安全的 any,可接受任意类型的值,只能赋值给 unknown 类型变量或 any 类型变量,使用前必须进行类型检查或断言
typescript 复制代码
let u: unknown = "hello";
// u.toUpperCase();  // 报错:必须先进行类型检查

// 正确做法:类型检查或断言
if (typeof u === "string") {
  u.toUpperCase();  // 安全
}
let str: string = u as string;  // 类型断言

never 类型

  • 作用:永远不会发生的值,不能赋值任何值,通常用于抛出异常或无限循环。几乎不用 never 去直接限制变量(无意义),该类型一般是 TS 主动推断出来
typescript 复制代码
// 限制函数的返回值:抛出异常或进入无限循环
function throwError(msg: string): never {
  throw new Error(msg);
}

void 类型

  • 作用:无返回值的函数,函数返回空(void)
  • 含义是【函数不返回任何值(返回值为空),调用者也不应依赖其返回值进行任何操作】,undefined 是 void 可以接受的一种"空"
  • 与 undefined 的区别:void 表示无返回值,undefined 表示变量未赋值
typescript 复制代码
function logMessage(msg: string): void {
  console.log(msg);
}

// 与 undefined 的区别
function returnUndefined(): undefined {
  return undefined;  // 必须返回 undefined
}

object 类型

  • 作用:小写,表示所有非原始类型(对象、函数、数组等),可接受任意对象值,也就是除了 number、string、boolean、symbol、null、undefined 以外的类型
  • 无论 object 还是 Object,因范围太大,实际开发使用较少
typescript 复制代码
let obj: object;
obj = {};           // OK
obj = [];           // OK
obj = () => {};     // OK
// obj = "hello";   // 报错:原始类型不能赋值

// 实际开发推荐具体类型
let person: { name: string; age: number } = {
  name: "张三",
  age: 18
};

tuple 类型

  • 作用:固定长度和类型的数组,每个元素的类型已知但可以不同。唯有元组不是关键字,用于精确描述一种特殊的数组的类型
typescript 复制代码
// 定义元组类型
let person: [string, number] = ["张三", 18];

// 可选元素
let optional: [string, number?] = ["张三"];  // OK

// 函数返回多个值
function getPerson(): [string, number] {
  return ["张三", 18];
}
const [name, age] = getPerson();

enum 类型

  • 作用:定义一组命名常量,增强代码可读性。枚举值默认从 0 开始,也可自定义值
typescript 复制代码
// 数字枚举(默认从 0 开始,也可自定义值),自动递增,且有反向映射(即可通过值来获取对应枚举成员名称)
enum Direction {
  Up,      // 0
  Down,    // 1
  Left,    // 2
  Right    // 3
}

// 字符串枚举(自定义枚举值为字符串)
enum Status {
  Success = "SUCCESS",
  Fail = "FAIL"
}

// 使用
function move(dir: Direction) {
  console.log(dir);
}
move(Direction.Up);  // 传入枚举值
  • 常量枚举:const 关键字定义,编译时会内联消除,避免生成额外代码(直接将枚举成员引用替换为实际值,不生成额外枚举对象)
  • 内联消除默认生效条件:用 tsc 编译 + 未开启 preserveConstEnums + 仅访问常量枚举的字面量成员 → 内联消除生效,无枚举对象,仅内联值。若没有生效可检查配置文件
typescript 复制代码
// 作用:常量枚举,编译时会内联消除,生成更高效的代码
const enum ConstDirection {
  Up,
  Down,
  Left,
  Right,
}

console.log(ConstDirection.Up); // 0
类型断言

告诉 TypeScript 你比它更了解类型:

typescript 复制代码
let value: unknown = "hello";
let str1: string = value as string;
let str2: string = <string>value; // 断言另一种写法(不是泛型)

2.5 函数返回值为void的特殊情况

typescript 复制代码
// 作用:演示void返回值的严格检查与宽松检查的区别
// 在函数定义时,限制函数返回值为void,那函数的返回值必须是空。
let LogFunc1 = function (): void {
  console.log("hello");
  // 除非返回undefined,返回其他值都报错
  // return 6
};

// 函数类型定义和赋值分两步时:"先用type声明限制函数返回值为void时,TS并不会严格要求函数返回空",即实际赋值的函数返回不严格要求为void类型。
type LogFunc2 = () => void;
let f0: LogFunc2 = () => 666;

// 为什么:为了确保以下代码成立,Array.prototype.push返回一个数字,而Array.prototype.forEach返回类型是void
let src = [1, 2, 3];
let dst = [0];
// 简写模式。为了简写模式的成立
src.forEach((el) => dst.push(el));
// 非简写模式时:回调函数返回值为隐式undefined时,符合forEach对回调函数返回值void类型的要求
src.forEach((el) => {
  dst.push(el);
});

3. 类型操作

两个用于自定义类型的方式:** ① type** 和 ** ② interface**

3.1 type

可为任意类型创建别名,让代码更简洁和可读性更强,方便类型复用和拓展。编译时起作用。编译成JS后会被移除。

  • 基本用法:用type关键字定义,type后跟类型名称
  • 联合类型:表示一个值可以是几种不同类型之一
  • 交叉类型:允许将多个类型合并为一个类型。合并后的类型将拥有所有被合并类型的成员。交叉类型通常用于对象类型。
typescript 复制代码
type gender1 = "男" | "女";
联合类型

表示一个值可以是几种不同类型之一:

typescript 复制代码
type Gender = "男" | "女";
let gender: Gender = "男";
交叉类型

将多个类型合并为一个类型:

typescript 复制代码
type Person = { name: string } & { age: number };
let person: Person = { name: "张三", age: 18 };

3.2 接口 interface

是一种定义结构的方式,主要作用为:类、对象、函数等规定一种锲约,这样可确保代码的一致性和类型安全,但要注意interface只能定义格式,不能包含任何实现。

3.2.1 基本语法
  • 接口用于定义对象的结构,为代码提供类型约束
  • 命名写法:interface IPerson{} 或者 interface PersonInterface{},通常用后者
  • 接口是TS中使用最多的
typescript 复制代码
// 作用:定义Person接口,包含必选属性、可选属性、只读属性和索引签名
interface Person {
  name: string;
  age?: number; // 可选属性
  readonly id: number; // 只读属性
  [key: string]: any; // 索引签名
}

let person: Person = {
  name: "张三",
  age: 18,
  id: 1,
  gender: "男", // 额外属性,通过索引签名允许
};
3.2.2 接口继承

一个interface继承另一个interface,从而实现代码复用:

typescript 复制代码
// 作用:演示接口继承,Dog接口继承Animal接口并添加新方法
interface Animal {
  name: string;
  speak(): void;
}

interface Dog extends Animal {
  bark(): void;
}

// 作用:类实现继承后的接口,必须实现所有属性和方法
class Labrador implements Dog {
  name: string;
  constructor(name: string) {
    this.name = name;
  }
  speak() {
    console.log("Woof!");
  }
  bark() {
    console.log("Bark!");
  }
}
3.2.3 接口合并

接口可以重复定义,TypeScript 会自动合并:

typescript 复制代码
// 作用:演示接口自动合并,同名接口会自动合并为一个接口
interface Widget {
  render(): void;
}

interface Widget {
  options: { theme: string };
}

// 合并后的接口
// interface Widget {
//   render(): void;
//   options: { theme: string };
// }
3.2.4 何时使用接口?
  • 定义对象的格式:描述数据模型、API响应格式、配置对象...等等,是开发中用的最多的场景
  • 类的契约:规定一个类需要实现哪些属性和方法
  • 自动合并:一般用于扩展第三方库的类型,这种特性在大型项目中可能会用到
3.2.5 interface和type的区别
  • 相同点:interface和type都可以用于定义对象结构,两者在许多场景中可以互换。(interface的合并和继承,type可以用交叉类型(&)实现)
  • 不同点
    • interface 更专注于定义对象和类的结构,支持继承、合并
    • type 可以定义类型别名、联合类型、交叉类型,但不支持继承和自动合并
3.2.6 interface和抽象类的区别
  • 相同点:都用于定义一个类的格式(应该遵循的契约)

  • 不同点

    • 接口:只能描述结构,不能有任何实现代码,一个类可以实现多个接口。(义父)
    • 抽象类:既可以包含抽象方法,也可以包含具体方法,一个类只能继承一个抽象类。(亲父)
  • 实现要求

    • 接口和抽象类的实现 / 继承都必须满足必填结构(接口是所有成员,抽象类是仅抽象成员),且都允许添加额外属性 / 方法;
    • 接口作为 "对象字面量类型" 时的 "多余属性报错" 是字面量严格检查规则,不是接口本身的限制,可通过变量赋值 / 类型断言规避;
    • 抽象类的具体方法无需子类实现,只有 abstract 修饰的抽象成员才必须实现,这是和接口最核心的区别之一。

注意:表述"接口的实现不能有额外的属性或方法"是不准确的。实际上,实现接口的类完全可以有额外的方法和属性。这个限制只适用于对象字面量的严格检查(excess property checking),而不是接口实现本身的限制。

4. 类

4.1 基本语法

typescript 复制代码
// 作用:定义Person类,包含属性和方法
class Person {
  name: string;
  age: number;

  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }

  speak() {
    console.log(`我是${this.name},今年${this.age}岁`);
  }
}

let person = new Person("张三", 18);
person.speak();

4.2 类的属性简写

typescript 复制代码
// 作用:使用构造函数参数简写,自动创建并初始化类属性
class Person {
  constructor(
    public name: string,
    protected age: number,
    private id: string,
    public readonly gender: string,
  ) {}
}

4.3 访问修饰符

修饰符 描述
public 默认值,可在类内部、子类、类外部访问
protected 可在类内部、子类访问
private 仅可在类内部访问
readonly 只读属性,属性无法修改

5. 抽象类

5.1 基本语法

抽象类是一种无法被实例化的类,专门用来定义类的结构和行为:

typescript 复制代码
// 作用:抽象类定义动物的基础行为和必须实现的抽象方法
abstract class Animal {
  abstract speak(): string;

  breathe() {
    return "Breathing...";
  }
}

// 作用:具体类继承抽象类,实现抽象方法
class Dog extends Animal {
  speak() {
    return "Bark";
  }
}

// let animal = new Animal(); // 错误:抽象类不能实例化
let dog = new Dog();
console.log(dog.speak()); // "Bark"
console.log(dog.breathe()); // "Breathing..."

5.2 何时使用抽象类

  1. 定义通用接口:为一组相关的类定义通用的行为(方法或属性)时
  2. 提供基础实现:在抽象类中提供某些方法或为其提供基础实现,这样派生类就可以继承这些实现
  3. 确保关键实现:强制派生类实现一些关键行为
  4. 共享代码和逻辑:当多个类需要共享部分代码时,抽象类可以避免代码重复

5.3 注意事项

  • 类继承时,重写父类的方法时建议用上override标识

6. 泛型

泛型允许在定义函数、类和接口时,使用类型参数来表示未指定的类型,这些参数在具体使用时,才被指定具体的类型,泛型能让同一段代码适用于多种类型,同时保持类型的安全性。

6.1 泛型函数

泛型用在函数上:

typescript 复制代码
// 作用:泛型函数,接收任意类型的数据并返回,保持类型一致性
function logData<T>(data: T): T {
  console.log(data);
  return data;
}

logData<number>(123);
logData<string>("hello");

// 泛型可以有多个
// 作用:多泛型参数函数,随机返回两个参数中的一个
function logData2<T, U>(data: T, data2: U): T | U {
  return Math.random() > 0.5 ? data : data2;
}

6.2 泛型接口

泛型用在接口上:

typescript 复制代码
// 作用:定义泛型接口,用于统一API响应格式,data字段类型可动态指定
interface Result<T> {
  success: boolean;
  data: T;
  message: string;
}

let result: Result<number> = {
  success: true,
  data: 100,
  message: "操作成功",
};

// 作用:泛型接口示例,gender字段类型可动态指定
interface PersonInterface3<T> {
  gender: T;
}

6.3 泛型类

泛型用在类上:

typescript 复制代码
// 作用:泛型容器类,可存储任意类型的数据,提供类型安全的添加和获取方法
class Container<T> {
  private items: T[] = [];

  add(item: T) {
    this.items.push(item);
  }

  get(index: number): T {
    return this.items[index];
  }
}

let numberContainer = new Container<number>();
numberContainer.add(1);
numberContainer.add(2);

// 作用:泛型类示例,extraInfo字段类型可动态指定
class PersonO<T> {
  constructor(public extraInfo: T) {}
}

new PersonO<number>(100);

7. 类型声明文件

主要作用是为现有的 JavaScript 代码提供类型信息,使得 TypeScript 能够在使用 JavaScript 库或模块时进行类型检查和提示。类型声明文件以 .d.ts 为扩展名:

7.1 基本语法

typescript 复制代码
// 作用:为 JavaScript 代码提供类型声明,使 TypeScript 能够识别 JS 代码的类型
// demo.d.ts
declare function add(a: number, b: number): number;
declare const PI: number;
declare interface Person {
  name: string;
  age: number;
}

7.2 使用

typescript 复制代码
// demo.js
function add(a, b) {
  return a + b;
}

const PI = 3.14159;

// 不需要引入 .d.ts 文件,TypeScript 会自动识别(后缀不同,但名称都是 demo)

7.3 注意事项

  • 使用时:.d.ts 扩展名不同于 .js,前面名称保持一致,如 demo.d.ts 和 demo.js
  • 不用在 .ts 文件中引入 .d.ts 文件,TypeScript 会自动识别并使用

8. 装饰器

装饰器是一种特殊类型的声明,它可以附加到类声明、方法、访问器、属性或参数上:

8.1 类装饰器

typescript 复制代码
// 作用:为类添加创建时间和获取时间的方法
function LogTime(target: Function) {
  return class extends target {
    createTime: Date;
    constructor(...args: any[]) {
      super(...args);
      this.createTime = new Date();
    }
    getTime() {
      return `该对象的创建时间是:${this.createTime}`;
    }
  };
}

@LogTime
class Person {
  name: string;
  constructor(name: string) {
    this.name = name;
  }
}

const person = new Person("张三");
console.log(person.getTime());

8.2 装饰器工厂

装饰器工厂是一个返回装饰器函数的函数,可以为装饰器添加参数:

typescript 复制代码
// 作用:工厂函数,根据参数n为类添加重复n次的自我介绍方法
function LogIntroduceFactory(n: number) {
  return function (target: Function) {
    target.prototype.introduce = function () {
      for (let i = 0; i < n; i++) {
        console.log(`我是${this.name}`);
      }
    };
  };
}

@LogIntroduceFactory(3)
class Person {
  name: string;
  constructor(name: string) {
    this.name = name;
  }
}

const person = new Person("张三");
person.introduce();

8.3 属性装饰器

属性装饰器,装饰类中的属性。在类的属性前面或上面写装饰器。

属性装饰器函数参数:类中分静态属性(static 修饰,类本身的属性)和实例属性(无 static 修饰,实例对象上的属性)

  • target:对于静态属性来说该值是类本身,对于实例属性来说该值是类的原型对象
  • propertyKey:属性名
typescript 复制代码
// 作用:为属性添加getter/setter,实现属性拦截和私有存储
function State(target: Object, propertyKey: string) {
  let key = `__${propertyKey}`;
  Object.defineProperty(target, propertyKey, {
    get() {
      return this[key];
    },
    set(newVal) {
      this[key] = newVal;
    },
  });
}

class Person {
  @State age: number;
  constructor(age: number) {
    this.age = age;
  }
}

8.4 方法装饰器

方法装饰器函数参数

  • target:对于静态方法来说该值是类本身,对于实例方法来说该值是类的原型对象
  • propertyKey:方法的名称
  • descriptor:方法的描述对象,其中 value 属性是被装饰的方法
typescript 复制代码
// 作用:为方法添加日志记录,在方法调用前后打印参数和返回值
function Logger(
  target: Object,
  propertyKey: string,
  descriptor: PropertyDescriptor,
) {
  const originalMethod = descriptor.value;
  descriptor.value = function (...args: any[]) {
    console.log(`${propertyKey}方法调用前,参数为:${args}`);
    const result = originalMethod.call(this, ...args);
    console.log(`${propertyKey}方法调用后,返回值为:${result}`);
    return result;
  };
}

class Person {
  @Logger
  speak(message: string) {
    console.log(`说:${message}`);
    return message;
  }
}

8.5 访问器装饰器

访问器装饰器:对 class 类的 get、set 方法进行装饰。class 的访问器属性 get、set 也区分实例属性和静态属性。

访问器装饰器函数参数

  • target:对于静态访问器来说该值是类本身,对于实例访问器来说该值是类的原型对象
  • propertyKey:访问器属性的名称
  • descriptor:访问器属性的描述对象
typescript 复制代码
// 作用:为setter添加范围验证,确保设置的值在指定范围内
function RangeValidate(min: number, max: number) {
  return function (
    target: Object,
    propertyKey: string,
    descriptor: PropertyDescriptor,
  ) {
    const originalSet = descriptor.set;
    descriptor.set = function (newVal: number) {
      if (newVal < min || newVal > max) {
        throw new Error(`${propertyKey}值必须在${min}到${max}之间`);
      }
      if (originalSet) {
        originalSet.call(this, newVal);
      }
    };
  };
}

class Weather {
  private _temp: number;

  @RangeValidate(-50, 50)
  set temp(newTemp: number) {
    this._temp = newTemp;
  }

  get temp() {
    return this._temp;
  }
}

8.6 参数装饰器

参数装饰器函数参数

  • target:如果修饰的是【静态方法】的参数,该值是类本身。如果修饰的是【实例方法】的参数,该值是类的【原型对象】
  • propertyKey:参数所在方法的名称
  • parameterIndex:参数的索引。参数在函数参数列表中的索引,从 0 开始
typescript 复制代码
// 作用:记录被装饰的参数信息(方法名和参数索引)
function Demo(target: Object, propertyKey: string, parameterIndex: number) {
  console.log(`参数装饰器:方法${propertyKey}的第${parameterIndex}个参数`);
}

class Person {
  speak(@Demo message: string) {
    console.log(`说:${message}`);
  }
}

8.7 装饰器的本质和特性

  • 装饰器的本质:是函数,入参是被装饰的类或实例对象
  • 类装饰器:是一个应用在类声明上的函数,可以为类添加额外的功能,或添加额外的逻辑 (注意:类装饰器在类定义时执行一次,不是在实例化时执行)
  • 关于返回值
    • 类装饰器有返回值:若类返回一个新的类,那这个类将替换掉被装饰的类
    • 装饰器无返回值:若类装饰器无返回值或返回undefined,那被装饰的类不会被替换
  • TypeScript的设计选择:不自动推断装饰器的返回类型,由开发者显式声明

8.8 构造函数类型

在TS中,Function类型所表示的范围十分广泛,包括:普通函数、箭头函数、方法等,但并非Function类型的函数都可以被new关键字实例化,例如箭头函数是不能被实例化的,那Ts中有两种方式声明一个构造函数:

  1. 仅声明构造类型

    typescript 复制代码
    type Constructor = new (...args: any[]) => any;
  2. 声明构造类型 + 指定静态属性

    typescript 复制代码
    type Constructor2 = {
      new (...args: any[]): any; // 表明该类型必须有一个构造函数
      wife: string; // 表明该类型必须有静态属性:wife
    };

8.9 装饰器组合

  • 装饰器可以组合使用,执行顺序为:先【由上到下】的执行所有的装饰器工厂,依次获取到装饰器,然后再【由下到上】的执行所有的装饰器函数
  • 组合应用:可以不同装饰器函数组合使用

8.10 装饰器执行顺序

完整执行顺序(从上到下):

  1. 参数装饰器(Parameter Decorator)
  2. 方法装饰器(Method Decorator)
  3. 访问器装饰器(Accessor Decorator)
  4. 属性装饰器(Property Decorator)
  5. 类装饰器(Class Decorator)

执行规则

  • 参数装饰器 → 方法装饰器/访问器装饰器 → 属性装饰器 → 类装饰器

同级别装饰器的执行规则

  • 装饰器工厂执行顺序:由上到下(工厂函数的执行顺序)
  • 装饰器函数执行顺序:由下到上(实际装饰器函数的执行顺序)

注意:实际执行时,对于同一个类,装饰器会按照从底层到上层的顺序执行,即先执行成员装饰器,最后执行类装饰器。

8.11 装饰器使用注意事项

  • 若需要接受参数就写成返回装饰器函数的工厂函数:@装饰器(参数),不接受参数就直接写装饰器:@装饰器
  • 装饰器不仅仅是覆盖一个原型上的方法,还可添加新的方法和状态等

9. 高级类型

9.1 类型守卫

typescript 复制代码
// 作用:类型守卫函数,判断值是否为字符串类型,帮助TypeScript缩小类型范围
function isString(value: unknown): value is string {
  return typeof value === "string";
}

function processValue(value: unknown) {
  if (isString(value)) {
    // 这里 TypeScript 知道 value 是 string 类型
    console.log(value.toUpperCase());
  }
}

9.2 条件类型

typescript 复制代码
// 作用:条件类型,根据类型参数是否为字符串返回不同的类型
type IsString<T> = T extends string ? true : false;

type A = IsString<string>; // true
type B = IsString<number>; // false

9.3 映射类型

typescript 复制代码
// 作用:映射类型,将接口的所有属性转换为只读属性
type Readonly<T> = {
  readonly [P in keyof T]: T[P];
};

interface Person {
  name: string;
  age: number;
}

type ReadonlyPerson = Readonly<Person>;
// { readonly name: string; readonly age: number; }

10. 学习资源

  • 学习时参考的资源有:TypeScript 官方文档,TypeScript Book,B站视频(禹神:三小时快速上手TypeScript,TS速通教程)

11. 总结

TypeScript 是一种强大的编程语言,它通过静态类型系统和面向对象编程特性,使 JavaScript 代码更加健壮和可维护。通过学习本资料,你应该已经掌握了 TypeScript 的核心概念和使用方法,包括类型系统、接口、类、泛型、装饰器等。

相关推荐
骑着小黑马2 小时前
从 Electron 到 Tauri 2:我用 3.5MB 做了个音乐播放器
前端·vue.js·typescript
ZengLiangYi19 小时前
并发 401 下的 Token 刷新竞态:一个被低估的 Bug
typescript
袋鱼不重1 天前
Typescript 核心概念
前端·typescript
刮涂层_赢大奖1 天前
我把 AI 编程 Agent 变成了宝可梦,让它们在像素风办公室里跑来跑去
前端·typescript·claude
时光不负努力2 天前
编程常用模式集合
前端·javascript·typescript
时光不负努力2 天前
ts+vue3开发规范
vue.js·typescript
时光不负努力2 天前
typescript常用的dom 元素类型
前端·typescript
时光不负努力2 天前
TS 常用工具类型
前端·javascript·typescript
Wect3 天前
LeetCode 210. 课程表 II 题解:Kahn算法+DFS 双解法精讲
前端·算法·typescript