Skip to content

类成员

TypeScript 提供了对 ES2015 中引入的 class 关键词的完全支持。

与其他 JavaScript 语言功能一样,TypeScript 增加了类型注释和其他语法,允许你表达类和其他类型之间的关系

这里有一个最基本的类——一个空的类:

typescript
class Point {}

这个类还不是很有用,所以我们开始添加一些成员。

类属性

在一个类上声明字段,创建一个公共的可写属性:A mapped type is a generic type which uses a union of PropertyKey s (frequently created via a keyof ) to iterate through keys to create a type:

typescript
class Point {
  x: number;
  y: number;
}

const pt = new Point();
pt.x = 0;
pt.y = 0;

与其他位置一样,类型注解是可选的,但如果不指定,将是一个隐含的 any 类型。 字段也可以有初始化器;这些初始化器将在类被实例化时自动运行。

typescript
class Point {
  x = 0;
  y = 0;
}

const pt = new Point();
// Prints 0, 0
console.log(`${pt.x}, ${pt.y}`);

就像 const 、 let 和 var 一样,一个类属性的初始化器将被用来推断其类型。

typescript
const pt = new Point();
pt.x = "0";
  • --strictPropertyInitialization

    strictPropertyInitialization 设置控制是否需要在构造函数中初始化类字段。

typescript
class BadGreeter {
  name: string;
}
typescript
class GoodGreeter {
  name: string;

  constructor() {
    this.name = "hello";
  }
}

请注意,该字段需要在构造函数本身中初始化。TypeScript 不会分析你从构造函数中调用的方法来检测初始化,因为派生类可能会覆盖这些方法而无法初始化成员。

如果你打算通过构造函数以外的方式来确定初始化一个字段(例如,也许一个外部库为你填充了你的类的一部分),你可以使用确定的赋值断言操作符 !

typescript
class OKGreeter {
  // 没有初始化,但没报错。
  name!: string;
}

readonly

字段的前缀可以是 readonly 修饰符。这可以防止在构造函数之外对该字段进行赋值。

typescript
class Greeter {
  readonly name: string = "world";

  constructor(otherName?: string) {
    if (otherName !== undefined) {
      this.name = otherName;
    }
  }

  err() {
    this.name = "not ok";
  }
}
const g = new Greeter();
g.name = "also not ok";

构造器

类构造函数与函数非常相似。你可以添加带有类型注释的参数、默认值和重载:

typescript
class Point {
  x: number;
  y: number;

  // 带默认值的正常签名
  constructor(x = 0, y = 0) {
    this.x = x;
    this.y = y;
  }
}
typescript
class Point {
  // 重载
  constructor(x: number, y: string);
  constructor(s: string);
  constructor(xs: any, y?: any) {
    // ...
  }
}

类的构造函数签名和函数签名之间只有一些区别:

  • 构造函数不能有类型参数--这属于外层类的声明,我们将在后面学习。
  • 构造函数不能有返回类型注释——类的实例类型总是被返回的。

Super 调用 就像在 JavaScript 中一样,如果你有一个基类,在使用任何 this. 成员之前,你需要在构造器主体中调用 super();

typescript
class Base {
  k = 4;
}

class Derived extends Base {
  constructor() {
    // 在ES5中打印一个错误的值;在ES6中抛出异常。
    console.log(this.k);
    super();
  }
}

在 JavaScript 中,忘记调用 super 是一个很容易犯的错误,但 TypeScript 会在必要时告诉你。

方法

一个类上的函数属性被称为方法。方法可以使用与函数和构造函数相同的所有类型注释。

typescript
class Point {
  x = 10;
  y = 10;

  scale(n: number): void {
    this.x *= n;
    this.y *= n;
  }
}

除了标准的类型注解,TypeScript 并没有为方法添加其他新的东西。

请注意,在一个方法体中,仍然必须通过 this 访问字段和其他方法。方法体中的非限定名称将总是指代包围范围内的东西。

typescript
let x: number = 0;

class C {
  x: string = "hello";

  m() {
    // 这是在试图修改第1行的'x',而不是类属性。
    x = "world";
  }
}

Getters / Setters

类也可以有访问器:

typescript
class C {
  _length = 0;
  get length() {
    return this._length;
  }
  set length(value) {
    this._length = value;
  }
}

请注意,一个没有额外逻辑的字段支持的 get/set 对在 JavaScript 中很少有用。如果你不需要在 get/set 操作中添加额外的逻辑,暴露公共字段也是可以的。

TypeScript 对访问器有一些特殊的推理规则:

  • 如果存在 get ,但没有 set ,则该属性自动是只读的
  • 如果没有指定 setter 参数的类型,它将从 getter 的返回类型中推断出来
  • 访问器和设置器必须有相同的成员可见性

从 TypeScript 4.3 开始,可以有不同类型的访问器用于获取和设置。

typescript
class Thing {
  _size = 0;

  get size(): number {
    return this._size;
  }

  set size(value: string | number | boolean) {
    let num = Number(value);

    // 不允许NaN、Infinity等

    if (!Number.isFinite(num)) {
      this._size = 0;
      return;
    }

    this._size = num;
  }
}

索引签名

类可以声明索引签名;这些签名的作用与其他对象类型的索引签名相同。

typescript
class MyClass {
  [s: string]: boolean | ((s: string) => boolean);

  check(s: string) {
    return this[s] as boolean;
  }
}

因为索引签名类型需要同时捕获方法的类型,所以要有用地使用这些类型并不容易。一般来说,最好将索引数据存储在另一个地方,而不是在类实例本身。