目录

TypeScript 语法详细总结

TypeScript 语法详细总结

TypeScript(简称TS)是JavaScript的强类型超集,在JS基础上新增静态类型系统和ES6+高级特性,编译后输出纯净的JS代码,可运行在任何支持JS的环境中。其核心价值是通过类型检查提前捕获代码错误,提升代码可维护性、可读性和健壮性。

本文从基础到进阶,系统梳理TS核心语法与特性,覆盖日常开发全场景。


一、核心基础:类型注解与类型推断

1. 类型注解

类型注解是TS最基础的语法,用于显式声明变量、函数、对象的类型,语法格式为标识符: 类型

// 变量类型注解
let name: string = "TS";
let age: number = 18;
let isDone: boolean = false;

// 函数参数与返回值注解
function add(a: number, b: number): number {
  return a + b;
}

2. 类型推断

TS具备强大的类型推断能力,当变量有初始赋值、函数有明确返回值时,无需手动写类型注解,TS会自动推导其类型。

let num = 123; // 自动推断为 number 类型
let str = "hello"; // 自动推断为 string 类型
const sum = (a: number, b: number) => a + b; // 返回值自动推断为 number

最佳实践:能稳定推断类型时,无需冗余注解;仅当推断不符合预期、无初始赋值时,手动添加注解。

3. 类型断言

用于手动指定值的类型,告诉TS编译器“我比你更清楚这个值的类型”,不会改变运行时行为,仅影响编译阶段。 两种语法格式:

// 1. as 语法(推荐,JSX环境下唯一可用)
const value: unknown = "hello world";
const strLength: number = (value as string).length;

// 2. 尖括号语法(非JSX环境可用)
const strLength2: number = (<string>value).length;

4. 非空断言 !

用于断言某个值一定不是 null/undefined,跳过编译器的空值检查。

const dom = document.getElementById("app");
dom!.innerText = "hello"; // 断言dom一定存在,不会报“可能为null”的错误

二、基础原始类型

TS完全兼容JS的原始类型,并扩展了专用的类型注解,是TS类型系统的最小单元。

类型 说明 示例
string 字符串类型 let a: string = "hi"
number 数字类型,支持整数、浮点数、进制数 let a: number = 100
boolean 布尔类型,仅 true/false let a: boolean = true
null 空值类型,仅 null let a: null = null
undefined 未定义类型,仅 undefined let a: undefined = undefined
symbol 唯一符号类型 let a: symbol = Symbol("key")
bigint 大整数类型 let a: bigint = 100n
void 无返回值类型,仅用于函数 function fn(): void {}
never 永不存在的值类型,用于永远不会正常返回的场景 function fn(): never { throw new Error() }
any 任意类型,关闭TS类型检查,可赋值给任何类型 let a: any = 123; a = "str"
unknown 类型安全的any,所有类型都可赋值给unknown,但unknown仅能赋值给unknown/any,必须类型缩小后才能操作 let a: unknown = 123; if(typeof a === "number") a++

关键注意点:

  1. 开启strictNullChecks(严格模式)后,nullundefined只能赋值给自身、unknownany,避免空值运行时错误。
  2. never是所有类型的子类型,可赋值给任何类型;但没有类型(除了never)可以赋值给never。
  3. 日常开发禁止滥用any,会丢失TS的类型保护能力,不确定类型时优先使用unknown

三、进阶复合类型

基于原始类型组合而成的复杂类型,用于描述更复杂的数据结构。

1. 数组类型

用于描述同类型元素的集合,有两种核心写法:

// 1. 类型[] 写法(推荐,简洁直观)
let numArr: number[] = [1, 2, 3];
let strArr: string[] = ["a", "b", "c"];

// 2. 数组泛型写法 Array<类型>
let boolArr: Array<boolean> = [true, false];

// 只读数组:禁止修改数组元素
const readonlyArr: readonly number[] = [1, 2, 3];
const readonlyArr2: ReadonlyArray<number> = [1, 2, 3];

2. 元组 Tuple

固定长度、固定每个位置类型的数组,严格限制元素数量和对应类型,越界访问会报错。

// 定义元组:第一个元素是string,第二个是number,长度固定为2
let user: [string, number] = ["张三", 18];

// 可选元素
let userInfo: [string, number?] = ["李四"]; // 第二个元素可选

// 剩余元素
let list: [string, ...number[]] = ["a", 1, 2, 3];

// 只读元组
const readonlyTuple: readonly [string, number] = ["a", 1];

3. 枚举 Enum

用于定义一组命名的常量,限制变量的取值范围,提升代码可读性。分为3种类型:

// 1. 数字枚举(默认,从0开始自增,可手动赋值)
enum Direction {
  Up, // 0
  Down, // 1
  Left = 10, // 手动赋值10
  Right, // 11(基于上一个值自增)
}
console.log(Direction.Up); // 0
console.log(Direction[0]); // 反向映射:Up

// 2. 字符串枚举(无反向映射)
enum Msg {
  Success = "操作成功",
  Fail = "操作失败",
}

// 3. 常量枚举 const enum(编译后会被内联,减少代码体积)
const enum Status {
  Enable = 1,
  Disable = 0,
}
const code = Status.Enable; // 编译后直接变为 const code = 1;

4. 联合类型 |

表示取值可以是多个类型中的任意一个,用|分隔。

let value: string | number;
value = "hello"; // 合法
value = 123; // 合法
// value = true; // 报错,不支持boolean类型

// 联合类型默认只能访问所有类型的共有属性
function getLength(arg: string | number[]) {
  return arg.length; // 合法,string和数组都有length属性
}

5. 交叉类型 &

多个类型合并为一个类型,新类型包含所有类型的属性和方法,用&分隔。

type Person = { name: string; age: number };
type Employee = { id: number; department: string };

// 合并两个类型,Staff 同时拥有 name、age、id、department 属性
type Staff = Person & Employee;

const staff: Staff = {
  name: "张三",
  age: 25,
  id: 1001,
  department: "技术部",
};

注意:若交叉类型中存在同名属性,且类型不兼容,会合并为never类型。

6. 字面量类型

将取值限定为一个或多个固定的字面量,常与联合类型搭配使用,实现枚举效果。

// 字符串字面量类型
type Direction = "up" | "down" | "left" | "right";
const move: Direction = "up"; // 合法,仅能取上述4个值

// 数字字面量类型
type StatusCode = 200 | 400 | 500;
const code: StatusCode = 200;

// 布尔字面量类型
type IsEnable = true | false;

四、接口 Interface

接口用于描述对象的形状(结构),定义对象需要包含的属性、方法及其类型,是TS中约束对象结构的核心方式。

1. 基础定义

// 定义Person接口,约束对象必须包含name、age属性
interface Person {
  name: string;
  age: number;
}

// 实现接口:必须严格匹配结构,不能缺少/多传未定义的属性
const user: Person = {
  name: "张三",
  age: 18,
};

2. 进阶属性修饰

interface User {
  readonly id: number; // 只读属性:声明时/构造函数中赋值后,不可修改
  name: string;
  age?: number; // 可选属性:该属性可以不存在
  [key: string]: any; // 索引签名:支持任意数量的额外属性,key为string,值为any
}

const user: User = {
  id: 1,
  name: "李四",
  gender: "男", // 索引签名支持的额外属性
};
user.id = 2; // 报错,只读属性不可修改

3. 接口描述函数

接口不仅可以描述对象,还可以约束函数的参数和返回值类型:

interface AddFunc {
  // 语法:(参数名: 参数类型) : 返回值类型
  (a: number, b: number): number;
}

const add: AddFunc = (a, b) => a + b;

4. 接口继承

接口可以通过extends实现继承,复用已有接口的属性定义,支持单继承和多继承。

interface Animal {
  name: string;
  eat(): void;
}

// 单继承
interface Dog extends Animal {
  bark(): void;
}

// 多继承
interface Cat extends Animal, Pet {
  miao(): void;
}

const husky: Dog = {
  name: "哈士奇",
  eat() { console.log("吃狗粮"); },
  bark() { console.log("汪汪叫"); },
};

5. 接口声明合并

同名接口会自动合并属性和方法,这是接口区别于类型别名type的核心特性,常用于扩展第三方库的类型。

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

// 合并后,User接口同时拥有name和age属性
const user: User = {
  name: "张三",
  age: 18,
};

五、类型别名 Type Alias

类型别名用于给任意类型起一个新名字,简化复杂类型的书写,比接口的适用范围更广。

1. 基础用法

// 给原始类型起别名
type Username = string;

// 给对象类型起别名
type User = {
  readonly id: number;
  name: string;
  age?: number;
};

// 给联合类型起别名
type Status = "pending" | "success" | "fail";

// 给函数类型起别名
type AddFunc = (a: number, b: number) => number;

2. Interface 与 Type 的核心区别

特性 Interface 接口 Type 类型别名
核心用途 描述对象/类的结构 给任意类型定义别名,支持所有类型
声明合并 支持同名接口自动合并 不支持同名类型别名,会报错
继承 extends实现继承 &交叉类型实现合并
支持类型 仅支持对象、函数、类 支持原始类型、联合、交叉、元组、字面量等所有类型
映射类型 不支持 支持in关键字实现映射类型

最佳实践:

  • 定义公共API、类的结构、需要扩展的类型,优先用interface
  • 定义联合、交叉、元组、复杂工具类型,优先用type

六、函数类型

TS对函数的参数、返回值、this、重载都提供了完整的类型支持。

1. 基础函数类型注解

// 命名函数:参数注解 + 返回值注解
function add(a: number, b: number): number {
  return a + b;
}

// 函数表达式
const add2: (a: number, b: number) => number = (a, b) => {
  return a + b;
};

// 无返回值函数:void
function log(msg: string): void {
  console.log(msg);
}

// 永远不返回的函数:never
function throwError(msg: string): never {
  throw new Error(msg);
}

2. 特殊参数处理

// 1. 可选参数:? ,必须放在必选参数之后
function greet(name: string, prefix?: string) {
  return prefix ? `${prefix} ${name}` : name;
}

// 2. 默认参数
function add(a: number, b: number = 0) {
  return a + b;
}

// 3. 剩余参数 ...rest
function sum(...nums: number[]): number {
  return nums.reduce((total, num) => total + num, 0);
}

3. 函数重载

函数重载用于定义同一个函数的多种入参类型和返回值类型,先写重载签名,再写实现签名。

// 重载签名:定义不同的入参和返回值
function getInfo(id: number): { id: number; name: string };
function getInfo(name: string): { id: number; name: string };

// 实现签名:必须兼容所有重载签名
function getInfo(value: number | string) {
  if (typeof value === "number") {
    return { id: value, name: "用户" + value };
  } else {
    return { id: 1, name: value };
  }
}

// 调用时,会根据入参类型匹配对应的重载签名
getInfo(1001);
getInfo("张三");

4. this 类型注解

TS支持显式指定函数中this的类型,避免this指向错误,this必须放在函数参数的第一位。

interface User {
  name: string;
  sayHi: () => void;
}

const user: User = {
  name: "张三",
  sayHi() {
    console.log(`Hi, ${this.name}`);
  },
};

// 显式指定this类型
function sayHi(this: User) {
  console.log(`Hi, ${this.name}`);
}

注意:箭头函数没有自己的this,无法给箭头函数指定this类型。


七、类 Class

TS在ES6类的基础上,扩展了访问修饰符、抽象类、接口实现等面向对象特性,完善了类的类型系统。

1. 基础类定义

class Person {
  // 属性类型注解
  name: string;
  age: number;

  // 构造函数参数注解
  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }

  // 方法参数与返回值注解
  sayHi(): void {
    console.log(`Hi, 我是${this.name},今年${this.age}岁`);
  }
}

const user = new Person("张三", 18);

2. 访问修饰符

用于控制类的属性和方法的访问权限,共3种:

修饰符 访问范围
public(默认) 类内部、子类、外部实例都可访问
private 仅类内部可访问,子类和外部实例都不可访问
protected 类内部、子类可访问,外部实例不可访问
class User {
  public name: string;
  private password: string;
  protected id: number;

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

class VipUser extends User {
  showId() {
    console.log(this.id); // 合法,protected子类可访问
    console.log(this.password); // 报错,private子类不可访问
  }
}

const user = new User("张三", "123456", 1001);
console.log(user.name); // 合法
console.log(user.password); // 报错,private外部不可访问
console.log(user.id); // 报错,protected外部不可访问

3. 核心修饰符

class User {
  readonly id: number; // 只读属性:仅声明时/构造函数中可赋值
  static platform: string = "WEB"; // 静态属性:属于类本身,而非实例
  static getPlatform(): string { // 静态方法
    return User.platform;
  }

  constructor(id: number) {
    this.id = id;
  }
}

console.log(User.platform); // 静态成员通过类名访问
console.log(User.getPlatform());

4. 参数属性

构造函数参数前添加访问修饰符,可快速定义并初始化类的属性,简化代码:

// 简化前
class User {
  public name: string;
  private age: number;
  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }
}

// 简化后(参数属性)
class User {
  constructor(
    public name: string,
    private age: number
  ) {}
}

5. 抽象类 abstract

抽象类是不能被实例化的基类,用于定义子类必须实现的属性和方法,作为派生类的模板。

// 定义抽象类
abstract class Animal {
  abstract name: string; // 抽象属性:子类必须实现
  abstract eat(): void; // 抽象方法:子类必须实现,不能有方法体

  // 普通方法:可直接复用
  sleep(): void {
    console.log("睡觉");
  }
}

// 子类继承抽象类,必须实现所有抽象属性和方法
class Dog extends Animal {
  name: string = "狗";
  eat(): void {
    console.log("吃狗粮");
  }
}

const dog = new Dog();
dog.eat();
dog.sleep();

6. 类实现接口 implements

类可以通过implements实现接口,强制类必须符合接口的结构,一个类可以实现多个接口。

interface Fly {
  fly(): void;
}
interface Run {
  run(): void;
}

// 实现多个接口
class Bird implements Fly, Run {
  fly() {
    console.log("飞翔");
  }
  run() {
    console.log("奔跑");
  }
}

八、泛型 Generic

泛型是TS实现类型复用、类型安全的核心特性,允许我们在定义函数、接口、类时,不预先指定具体类型,而是在使用时再指定类型,让代码更通用。

1. 泛型基础

// 泛型函数:T 是类型参数,调用时指定具体类型
function identity<T>(value: T): T {
  return value;
}

// 1. 手动指定类型
const str = identity<string>("hello");
const num = identity<number>(123);

// 2. 类型推断:TS自动根据入参推断类型参数
const bool = identity(true); // 自动推断 T 为 boolean

2. 泛型约束

通过extends关键字限制类型参数的范围,确保类型参数必须包含指定的属性/方法。

// 约束:传入的参数必须有 length 属性
interface HasLength {
  length: number;
}

function getLength<T extends HasLength>(value: T): number {
  return value.length;
}

getLength("hello"); // 合法,字符串有length
getLength([1, 2, 3]); // 合法,数组有length
getLength(123); // 报错,数字没有length

3. 泛型接口与泛型类

// 泛型接口
interface ApiResponse<T> {
  code: number;
  msg: string;
  data: T; // 泛型数据,由调用方指定类型
}

// 使用:指定data为用户对象类型
const userRes: ApiResponse<{ name: string; id: number }> = {
  code: 200,
  msg: "success",
  data: { name: "张三", id: 1001 },
};

// 泛型类
class Container<T> {
  value: T;
  constructor(value: T) {
    this.value = value;
  }
  getValue(): T {
    return this.value;
  }
}

const numberContainer = new Container<number>(123);

4. 泛型默认类型

给泛型参数设置默认类型,调用时若未指定类型,会使用默认类型。

interface ApiResponse<T = any> {
  code: number;
  msg: string;
  data: T;
}

// 未指定类型,默认使用 any
const res: ApiResponse = {
  code: 200,
  msg: "success",
  data: {},
};

5. TS 内置泛型工具类型

TS内置了大量常用的泛型工具类型,基于泛型实现,用于快速转换类型,以下是最常用的工具类型:

工具类型 作用 示例
Partial<T> 将T的所有属性变为可选 type PartialUser = Partial<{ name: string }>
Required<T> 将T的所有属性变为必选 type RequiredUser = Required<{ name?: string }>
Readonly<T> 将T的所有属性变为只读 type ReadonlyUser = Readonly<{ name: string }>
Pick<T, K> 从T中挑选指定的K属性,构建新类型 type UserName = Pick<User, "name">
Omit<T, K> 从T中排除指定的K属性,构建新类型 type UserWithoutAge = Omit<User, "age">
Exclude<T, U> 从T中排除可分配给U的类型,用于联合类型 `type T = Exclude<“a”
Extract<T, U> 从T中提取可分配给U的类型,用于联合类型 `type T = Extract<“a”
ReturnType<T> 获取函数T的返回值类型 type T = ReturnType<() => string> // string
Parameters<T> 获取函数T的参数类型,组成元组 type T = Parameters<(a:number)=>void> // [number]
Awaited<T> 获取Promise resolve的类型,用于异步场景 type T = Awaited<Promise<string>> // string
Record<K, T> 构建key为K类型,value为T类型的对象类型 type UserMap = Record<number, User>

九、类型守卫与类型缩小

类型缩小是指通过条件判断,将宽泛的类型(如联合类型、unknown)缩小到更具体的类型,让TS编译器精准识别类型,避免报错。类型守卫是实现类型缩小的核心手段。

1. 常用类型守卫方式

// 1. typeof 守卫:用于原始类型
function print(value: string | number) {
  if (typeof value === "string") {
    console.log(value.length); // 缩小为string类型
  } else {
    console.log(value.toFixed(2)); // 缩小为number类型
  }
}

// 2. instanceof 守卫:用于类实例
class Dog { bark() {} }
class Cat { miao() {} }
function animalAction(animal: Dog | Cat) {
  if (animal instanceof Dog) {
    animal.bark();
  } else {
    animal.miao();
  }
}

// 3. in 操作符守卫:判断对象是否包含指定属性
interface User { name: string }
interface Admin { name: string; permission: string[] }
function checkAuth(account: User | Admin) {
  if ("permission" in account) {
    console.log(account.permission); // 缩小为Admin类型
  }
}

// 4. 等值判断守卫:=== / !==
function getValue(value: string | null | undefined) {
  if (value !== null && value !== undefined) {
    console.log(value.length); // 缩小为string类型
  }
}

// 5. 真值判断守卫:if() 排除 falsy 值
function printMsg(msg: string | undefined | null) {
  if (msg) {
    console.log(msg.toUpperCase()); // 缩小为string类型
  }
}

2. 自定义类型守卫

通过类型谓词 parameterName is Type 自定义类型守卫,实现复杂的类型判断。

// 自定义守卫:判断值是否为string类型
function isString(value: unknown): value is string {
  return typeof value === "string";
}

// 自定义守卫:判断值是否为合法的用户对象
interface User {
  name: string;
  id: number;
}
function isUser(value: unknown): value is User {
  return (
    typeof value === "object" &&
    value !== null &&
    "name" in value &&
    "id" in value
  );
}

// 使用
function handleValue(value: unknown) {
  if (isString(value)) {
    console.log(value.length);
  }
  if (isUser(value)) {
    console.log(value.name);
  }
}

3. 可辨识联合

可辨识联合是TS中最常用的类型缩小技巧,核心是:联合类型的每个成员都包含一个共同的字面量属性(可辨识标签),通过该标签缩小类型。

// 定义可辨识联合:每个类型都有共同的type属性
interface Circle {
  type: "circle"; // 可辨识标签
  radius: number;
}
interface Square {
  type: "square"; // 可辨识标签
  sideLength: number;
}
type Shape = Circle | Square;

// 通过type标签缩小类型
function getArea(shape: Shape): number {
  switch (shape.type) {
    case "circle":
      return Math.PI * shape.radius ** 2; // 缩小为Circle类型
    case "square":
      return shape.sideLength ** 2; // 缩小为Square类型
  }
}

十、高级类型特性

1. 索引类型

  • keyof T:索引类型查询,获取类型T的所有属性名,组成联合类型
  • T[K]:索引访问类型,获取类型T中属性K对应的类型
interface User {
  name: string;
  age: number;
  id: number;
}

// keyof 获取所有属性名联合类型
type UserKeys = keyof User; // "name" | "age" | "id"

// 索引访问类型
type UserNameType = User["name"]; // string
type UserIdType = User["id"]; // number

2. 映射类型

基于已有类型,通过in关键字遍历属性,创建新类型,TS内置工具类型大多基于映射类型实现。

// 实现只读映射类型
type MyReadonly<T> = {
  readonly [P in keyof T]: T[P]; // 遍历T的所有属性,添加readonly修饰
};

// 实现可选映射类型
type MyPartial<T> = {
  [P in keyof T]?: T[P]; // 遍历T的所有属性,添加?可选修饰
};

3. 条件类型

基于三元表达式实现类型的条件判断,语法:T extends U ? X : Y,若T可分配给U,返回X类型,否则返回Y类型。

// 基础条件类型
type IsString<T> = T extends string ? true : false;
type T1 = IsString<string>; // true
type T2 = IsString<number>; // false

// 分布式条件类型:当T是联合类型时,会遍历每个成员执行判断
type MyExclude<T, U> = T extends U ? never : T;
type T3 = MyExclude<"a" | "b" | "c", "a" | "b">; // "c"

4. 模板字面量类型

基于字符串字面量,通过${}嵌入类型,实现字符串类型的动态拼接,TS还提供了内置的字符串操作类型。

// 基础模板字面量
type Direction = "up" | "down";
type DirectionEvent = `on${Capitalize<Direction>}`; // "onUp" | "onDown"

// 内置字符串工具类型
type S1 = Uppercase<"hello">; // "HELLO"
type S2 = Lowercase<"HELLO">; // "hello"
type S3 = Capitalize<"hello">; // "Hello"
type S4 = Uncapitalize<"Hello">; // "hello"

5. 常用操作符

// 1. 可选链操作符 ?.:安全访问嵌套属性,避免Cannot read properties of undefined
const userName = user?.info?.name;

// 2. 空值合并操作符 ??:仅当左侧为null/undefined时,返回右侧值
const pageSize = query.pageSize ?? 10;

// 3. satisfies 操作符:验证值是否符合类型,同时保留值的字面量类型
type Config = Record<string, string | number>;
const config = {
  host: "localhost",
  port: 3000,
} satisfies Config;
config.port.toFixed(2); // 保留了port的number类型,不会被拓宽为string|number

十一、模块与命名空间

1. ES模块

TS完全兼容ES6的import/export模块规范,支持类型的单独导入导出。

// 导出类型和值
export type User = { name: string };
export const user = { name: "张三" };

// 导入类型和值
import { User, user } from "./user";

// 仅导入类型(编译后会被移除,减少体积,避免循环引用)
import type { User } from "./user";

2. 模块声明

用于给无类型的第三方JS库补充类型声明,通常写在.d.ts声明文件中。

// 给第三方JS模块声明类型
declare module "untyped-js-lib" {
  export function add(a: number, b: number): number;
  export const version: string;
}

// 全局类型声明
declare global {
  interface Window {
    $: JQuery; // 给window扩展$属性
  }
}

3. 命名空间 namespace

TS早期的模块化方案,用于在全局作用域内隔离代码,避免命名冲突,现在推荐优先使用ES模块,仅在特殊场景使用。

namespace Utils {
  export function add(a: number, b: number) {
    return a + b;
  }
  export const version = "1.0.0";
}

// 访问命名空间内的成员
console.log(Utils.add(1, 2));
console.log(Utils.version);

十二、装饰器 Decorator

装饰器是一种特殊类型的声明,用于扩展类、方法、属性、参数的功能,是AOP(面向切面编程)的核心实现方式。TS支持两种装饰器模式:实验性装饰器ES标准装饰器

1. 开启装饰器支持

tsconfig.json中开启配置:

{
  "compilerOptions": {
    // 实验性装饰器
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    // ES标准装饰器(TS 5.0+ 支持)
    "experimentalDecorators": false,
    "target": "ES2022"
  }
}

2. 装饰器分类(实验性)

// 1. 类装饰器:作用于类,参数是类的构造函数
function ClassDecorator(target: Function) {
  console.log("类装饰器", target);
}

// 2. 方法装饰器:作用于类的方法
function MethodDecorator(
  target: any,
  propertyKey: string,
  descriptor: PropertyDescriptor
) {
  console.log("方法装饰器", propertyKey);
}

// 3. 属性装饰器:作用于类的属性
function PropertyDecorator(target: any, propertyKey: string) {
  console.log("属性装饰器", propertyKey);
}

// 4. 参数装饰器:作用于方法的参数
function ParamDecorator(target: any, propertyKey: string, parameterIndex: number) {
  console.log("参数装饰器", parameterIndex);
}

// 使用装饰器
@ClassDecorator
class User {
  @PropertyDecorator
  name: string;

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

  @MethodDecorator
  sayHi(@ParamDecorator msg: string) {
    console.log(msg);
  }
}

3. 装饰器工厂

返回装饰器的函数,支持传入自定义参数,灵活控制装饰器的行为。

// 装饰器工厂
function Log(msg: string) {
  return function (target: Function) {
    console.log(`${msg}${target.name}`);
  };
}

@Log("用户类被创建")
class User {}

十三、核心配置与严格模式

TS的行为由tsconfig.json配置文件控制,其中严格模式是保证类型安全的核心,以下是核心配置项:

{
  "compilerOptions": {
    // 编译目标JS版本
    "target": "ES2020",
    // 模块系统
    "module": "ESNext",
    // 模块解析策略
    "moduleResolution": "NodeNext",
    // 开启严格模式(包含以下所有严格检查子项)
    "strict": true,
    // 严格空值检查,禁止null/undefined赋值给非空类型
    "strictNullChecks": true,
    // 禁止隐式any类型
    "noImplicitAny": true,
    // 严格检查函数的this类型
    "strictFunctionTypes": true,
    // 严格检查类的属性初始化
    "strictPropertyInitialization": true,
    // 兼容ES模块和CommonJS
    "esModuleInterop": true,
    // 跳过库文件类型检查
    "skipLibCheck": true,
    // 生成类型声明文件
    "declaration": true
  },
  // 编译包含的文件
  "include": ["src/**/*"],
  // 编译排除的文件
  "exclude": ["node_modules"]
}

最佳实践:新项目必须开启strict: true严格模式,最大化TS的类型检查能力,提前规避运行时错误。