Object 及其实例提供的内置接口在本章中,我们将研究与类及其实例相关的 TypeScript 类型。
考虑以下类
class Counter extends Object {
static createZero() {
return new Counter(0);
}
value: number;
constructor(value: number) {
super();
this.value = value;
}
increment() {
this.value++;
}
}
// Static method
const myCounter = Counter.createZero();
assert.ok(myCounter instanceof Counter);
assert.equal(myCounter.value, 0);
// Instance method
myCounter.increment();
assert.equal(myCounter.value, 1);图 2 中的图表显示了类 Counter 的运行时结构。该图中有两条对象原型链
Counter 的对象组成。类 Counter 的原型对象是其超类 Object。myCounter 的对象组成。该链从实例 myCounter 开始,然后是 Counter.prototype(包含类 Counter 的原型方法)和 Object.prototype(包含类 Object 的原型方法)。在本章中,我们将首先探讨实例对象,然后探讨作为对象的类。
接口指定对象提供的服务。例如
interface CountingService {
value: number;
increment(): void;
}TypeScript 的接口按 结构 工作:为了使对象实现接口,它只需要具有正确类型和正确名称的属性。我们可以在以下示例中看到这一点
const myCounter2: CountingService = new Counter(3);结构化接口很方便,因为即使对于已经存在的对象,我们也可以为其创建接口(即,我们可以在事后引入它们)。
如果我们事先知道某个对象必须实现某个给定的接口,那么尽早检查它是否实现了该接口通常是有意义的,以避免以后出现意外。我们可以通过 implements 对类实例执行此操作
class Counter implements CountingService {
// ···
};注释
.increment)和自身属性(例如 .value)。类本身也是对象(函数)。因此,我们可以使用接口来指定它们的属性。这里的主要用例是描述对象的工厂。下一节将给出一个示例。
以下两个接口可用于支持其实例与 JSON 相互转换的类
// Converting JSON to instances
interface JsonStatic {
fromJson(json: any): JsonInstance;
}
// Converting instances to JSON
interface JsonInstance {
toJson(): any;
}我们在以下代码中使用这些接口
class Person implements JsonInstance {
static fromJson(json: any): Person {
if (typeof json !== 'string') {
throw new TypeError(json);
}
return new Person(json);
}
name: string;
constructor(name: string) {
this.name = name;
}
toJson(): any {
return this.name;
}
}这就是我们可以立即检查类 Person(作为对象)是否实现了接口 JsonStatic 的方法
// Assign the class to a type-annotated variable
const personImplementsJsonStatic: JsonStatic = Person;以下进行此检查的方法似乎是个好主意
const Person: JsonStatic = class implements JsonInstance {
// ···
};但是,这实际上行不通
Person 进行 new 调用,因为 JsonStatic 没有构造函数签名。Person 具有 .fromJson() 之外的静态属性,TypeScript 将不允许我们访问它们。Object 及其实例提供的内置接口查看 TypeScript 的内置类型是有益的
一方面,接口 ObjectConstructor 用于类 Object 本身
/**
* Provides functionality common to all JavaScript objects.
*/
declare var Object: ObjectConstructor;
interface ObjectConstructor {
new(value?: any): Object;
(): any;
(value: any): any;
/** A reference to the prototype for a class of objects. */
readonly prototype: Object;
/**
* Returns the prototype of an object.
* @param o The object that references the prototype.
*/
getPrototypeOf(o: any): any;
}另一方面,接口 Object 用于 Object 的实例
interface Object {
/** The initial value of Object.prototype.constructor is the standard built-in Object constructor. */
constructor: Function;
/** Returns a string representation of an object. */
toString(): string;
}名称 Object 在两个不同的 语言级别 使用了两次
考虑以下类
class Color {
name: string;
constructor(name: string) {
this.name = name;
}
}此类定义创建了两个东西。
首先,一个名为 Color 的构造函数(可以通过 new 调用)
assert.equal(
typeof Color, 'function')其次,一个名为 Color 的接口,它与 Color 的实例相匹配
const green: Color = new Color('green');以下是 Color 确实是一个接口的证明
interface RgbColor extends Color {
rgbValue: [number, number, number];
}不过,有一个陷阱:使用 Color 作为静态类型并不是一个非常严格的检查
class Color {
name: string;
constructor(name: string) {
this.name = name;
}
}
class Person {
name: string;
constructor(name: string) {
this.name = name;
}
}
const person: Person = new Person('Jane');
const color: Color = person; // (A)为什么 TypeScript 在 A 行没有报错?这是由于结构类型化:Person 和 Color 的实例具有相同的结构,因此在静态上是兼容的。
我们可以通过添加私有属性来使这两组对象不兼容
class Color {
name: string;
private branded = true;
constructor(name: string) {
this.name = name;
}
}
class Person {
name: string;
private branded = true;
constructor(name: string) {
this.name = name;
}
}
const person: Person = new Person('Jane');
// @ts-expect-error: Type 'Person' is not assignable to type 'Color'.
// Types have separate declarations of a private property
// 'branded'. (2322)
const color: Color = person;在这种情况下,私有属性会关闭结构类型化。