首页 / TypeScript 入门教程 / 静态成员

TypeScript 入门教程

静态成员

本教程共 80 篇 · 第 35 篇 · 更新于 2026-08-10 · 约 10 分钟阅读

TypeScriptTypeScript 入门教程static静态成员静态块

本节目标:掌握 static 属性、方法和静态块的使用,理解静态成员属于类本身而非实例,学会用静态工厂方法等实用模式。

前面四章讲的都是实例成员——每个 new 出来的对象有自己的属性、自己的方法。但有些东西是所有实例共享的,或者跟实例根本没关系,这时候就该用静态成员了。

静态属性

static 关键字声明的属性属于类本身,不属于任何实例:

class Counter {
  static count = 0;    // 静态属性:属于类

  constructor() {
    Counter.count++;   // 通过类名访问
  }
}

const c1 = new Counter();
const c2 = new Counter();
const c3 = new Counter();

console.log(Counter.count); // 3 —— 直接通过类名访问
// console.log(c1.count);    // ❌ 实例上访问不到

Counter.count 是存储在类上的,所有实例共享同一个值。这跟实例属性完全不同——每个实例的属性是独立的。

静态方法

静态方法同样属于类,通过类名调用:

class MathUtils {
  static clamp(value: number, min: number, max: number): number {
    return Math.max(min, Math.min(value, max));
  }

  static lerp(a: number, b: number, t: number): number {
    return a + (b - a) * t;
  }
}

console.log(MathUtils.clamp(5, 0, 10));      // 5
console.log(MathUtils.clamp(-3, 0, 10));     // 0
console.log(MathUtils.clamp(15, 0, 10));     // 10
console.log(MathUtils.lerp(0, 100, 0.5));    // 50

静态方法最常见的用途就是工具函数集合。你不需要创建实例就能用它们。

Note

如果你来自 C# 或 Java,可能会想写一个”静态类”——所有成员都是 static 的类。在 TypeScript 里不需要这样做。直接用一个普通对象或者模块的顶层函数就够了。TypeScript 没有 static class 语法。

静态成员也可以有访问修饰符

static 可以和 publicprotectedprivate 组合:

class Database {
  private static connection: string | null = null;

  static connect(url: string): void {
    Database.connection = url;
    console.log(`已连接到 ${url}`);
  }

  static getConnection(): string {
    if (Database.connection === null) {
      throw new Error("未连接数据库");
    }
    return Database.connection;
  }
}

Database.connect("postgres://localhost:5432/mydb");
console.log(Database.getConnection());
// console.log(Database.connection);  // ❌ private 不能外部访问

静态成员的 this 指向

静态方法里的 this 指向的是类本身,而不是实例:

class MyClass {
  static x = 0;

  static printX(): void {
    console.log(this.x);   // this 指向 MyClass 这个构造函数
  }
}

MyClass.printX(); // 0

这个特性在继承时特别有用:

class Base {
  static greeting = "Hello";

  static greet(): void {
    console.log(this.greeting);
  }
}

class Derived extends Base {
  static greeting = "你好";
}

Base.greet();    // "Hello"
Derived.greet();  // "你好" —— this 指向的是 Derived!

因为 Derived.greet() 是通过 Derived 调用的,所以 this 指向 Derived,访问的是 Derived.greeting

静态成员被继承

子类可以继承父类的静态成员:

class Base {
  static getClassName(): string {
    return "Base";
  }
}

class Derived extends Base {
  // 继承了 getClassName()
}

console.log(Derived.getClassName()); // "Base"

注意这里 Derived.getClassName() 可以正常调用,但返回值是 "Base"——因为方法体里硬编码了返回字符串。如果你想让方法感知当前类,可以用 this.constructor.name,但在静态方法里 this 指向的是类本身(构造函数),所以得用 this.name

class Base {
  static getClassName(): string {
    return this.name; // this 指向调用时的类
  }
}

class Derived extends Base {}

console.log(Base.getClassName());    // "Base"
console.log(Derived.getClassName());  // "Derived"

静态块

从 ES2022(TypeScript 4.4+)开始,类里可以写静态块(static block)。它是一段在类定义时立即执行的代码,有自己的作用域,能访问类的私有字段:

class Config {
  static #apiUrl: string;

  static {
    // 复杂的初始化逻辑可以放在这里
    try {
      const env = process.env.NODE_ENV;
      Config.#apiUrl = env === "production"
        ? "https://api.prod.example.com"
        : "https://api.dev.example.com";
    } catch {
      Config.#apiUrl = "https://api.dev.example.com";
    }
  }

  static getApiUrl(): string {
    return Config.#apiUrl;
  }
}

console.log(Config.getApiUrl());

静态块适合的场景:

  • 静态属性需要复杂的初始化逻辑(多行、条件判断、try-catch)。
  • 需要访问私有静态字段来做初始化。
  • 多个静态属性之间存在依赖关系,需要按顺序初始化。
Tip

如果静态属性只用一个表达式就能初始化,直接在声明时赋值就行,不需要静态块。静态块是用来处理”一个表达式搞不定”的情况。

静态工厂方法

静态方法的一个经典用法是静态工厂方法——用静态方法创建实例,而不是直接 new

class User {
  constructor(
    public readonly id: number,
    public readonly name: string,
    public readonly role: string
  ) {}

  static createAdmin(name: string): User {
    return new User(Math.floor(Math.random() * 10000), name, "admin");
  }

  static createGuest(): User {
    return new User(0, "Guest", "guest");
  }

  static fromJSON(json: string): User {
    const data = JSON.parse(json);
    return new User(data.id, data.name, data.role);
  }
}

const admin = User.createAdmin("Alice");
console.log(admin.role); // "admin"

const guest = User.createGuest();
console.log(guest.name); // "Guest"

const user = User.fromJSON('{"id": 1, "name": "Bob", "role": "user"}');
console.log(user.name); // "Bob"

工厂方法比直接 new 好在:

  1. 命名清晰User.createAdmin("Alice")new User(Math.random(), "Alice", "admin") 更易读。
  2. 可以加逻辑:创建之前做校验、创建之后做额外处理。
  3. 可以返回子类:工厂方法内部可以根据条件返回不同的子类实例。

静态成员不能做的事

不能用实例访问

class Example {
  static value = 42;
}

const e = new Example();
// console.log(e.value);  // ❌ 实例上不存在

泛型类的静态成员不能引用类型参数

class Box<T> {
  // static defaultValue: T;  // ❌ 静态成员不能引用类泛型参数
}

为什么?因为类型在运行时被擦除了。Box<string>Box<number> 在运行时的 Box.defaultValue 是同一个内存槽——如果 T 可以用,到底该是 string 还是 number?所以 TypeScript 直接禁止。

特殊的静态名称

类的静态成员不能跟 Function.prototype 上的属性重名。namelengthcallapplybind 等都不能用作静态成员名:

class S {
  // static name = "S";  // ❌ 跟 Function.name 冲突
}

小结

  • static 成员属于类本身,通过类名访问,不通过实例。
  • 静态方法里的 this 指向调用时的类(构造函数)。
  • 静态成员可以被继承,this 在继承链中指向调用类。
  • 静态块 static {} 用于复杂的类初始化逻辑。
  • 静态工厂方法提供更语义化的创建实例方式。
  • 泛型类的静态成员不能引用类型参数。
  • 静态名不能跟 Function.prototype 的固有属性冲突。

类本身的结构已经讲得差不多了。接下来要解决一个关键问题:类怎么跟接口配合——类实现接口。