Skip to content

存取器属性

在 TypeScript 中,存取器(Getter/Setter)的语法与 JavaScript 几乎一致。通过存取器,我们可以截获对对象成员的访问和设置操作,从而实现更精细的控制(如数据验证、副作用处理等)。

基本用法

typescript
class Animal {
  // 私有属性,外部无法直接访问
  private _age: number;

  // ES2022 私有字段
  #type: string;

  constructor(
    public name: string,
    protected color: string,
    age: number,
    type: string,
  ) {
    this._age = age;
    this.#type = type;
  }

  // Getter: 获取 age
  get age() {
    return this._age;
  }

  // Setter: 设置 age,包含验证逻辑
  set age(value: number) {
    if (value < 0 || value > 150) {
      throw new Error('年龄不符合规范...');
    }
    this._age = value;
  }

  // Getter: 获取 type
  get type(): string {
    return this.#type;
  }

  // Setter: 设置 type,限制特定字符串
  set type(value: 'Cat' | 'Dog') {
    this.#type = value;
  }
}

const a = new Animal('小白', '白色', 3, 'Dog');

// 访问 Getter
console.log(a.age); // 3

// 触发 Setter (验证失败)
// a.age = -1; // Error: 年龄不符合规范...

// 触发 Setter (成功)
a.type = 'Cat';

提示

如果类中只定义了 get 而没有 set,则该属性会自动被推断为 readonly

常见陷阱:可选属性与存取器

当我们将私有属性定义为可选属性(?)并配合存取器使用时,常常会遇到类型冲突的问题。

问题复现

typescript
class Animal {
  // 可选属性:类型为 number | undefined
  private _age?: number;

  // Getter 推断返回类型为 number
  get age() {
    // Error: 不能将类型“number | undefined”分配给类型“number”。
    return this._age;
  }

  set age(value: number) {
    this._age = value;
  }
}

报错原因: 开启 strictNullChecks(严格空值检查)后,_age 可能是 undefined,而 Getter 声明(或推断)的返回类型通常是确定的 number,二者不兼容。

解决方案

方案一:移除可选标记(推荐)

如果属性本来就不应该是空的,直接移除 ?,确保在构造函数中初始化。

typescript
class Animal {
  private _age: number = 0; // 给定初始值
}

方案二:移除 Setter

如果只需要读取,不需要修改,可以移除 Setter。这样 Getter 的返回值类型会自动推断包含 undefined

方案三:在 Getter 中处理 undefined

确保 Getter 始终返回确定的类型。

typescript
get age(): number {
  if (this._age === undefined) {
    throw new Error('年龄未知');
  }
  return this._age;
}

方案四:非空断言(慎用)

如果你确定访问时 _age 一定有值,可以使用 ! 断言。

typescript
get age(): number {
  return this._age!;
}

方案五:调整 TS 配置(不推荐)

tsconfig.json 中设置 "strictNullChecks": false。这会关闭整个项目的严格空值检查,可能会引入更多隐患,通常不建议这样做。

json
{
  "compilerOptions": {
    "strictNullChecks": false
  }
}