Object
及其實例在這個關於 TypeScript 的章節中,我們會探討與類別及其實例相關的類型。
考慮這個類別
class Counter extends Object {createZero() {
static new Counter(0);
return
}: number;
valueconstructor(value: number) {
super();
.value = value;
this
}increment() {
.value++;
this
}
}// Static method
= Counter.createZero();
const myCounter .ok(myCounter instanceof Counter);
assert.equal(myCounter.value, 0);
assert
// Instance method
.increment();
myCounter.equal(myCounter.value, 1); assert
圖 2 中的圖表顯示類別 Counter
的執行時期結構。此圖表中有兩個物件的原型鏈
Counter
的物件。類別 Counter
的原型物件是其超類別 Object
。myCounter
的物件。此鏈從實例 myCounter
開始,然後繼續到 Counter.prototype
(包含類別 Counter
的原型方法)和 Object.prototype
(包含類別 Object
的原型方法)。在本章中,我們將首先探討實例物件,然後探討物件中的類別。
介面會指定物件提供的服務。例如
interface CountingService {: number;
valueincrement(): void;
}
TypeScript 的介面以 結構化 方式運作:物件必須具備具備正確類型之正確屬性,才能實作介面。我們可以在以下範例中看到
: CountingService = new Counter(3); const myCounter2
結構化介面很方便,因為我們甚至可以為已存在的物件建立介面(亦即,我們可以在事後加入介面)。
如果我們事先知道物件必須實作特定介面,通常會在早期檢查物件是否實作介面,以避免之後出現意外。我們可以使用 implements
針對類別實例執行此操作
class Counter implements CountingService {// ···
; }
註解
.increment
)和自有屬性(例如 .value
)。類別本身也是物件(函式)。因此,我們可以使用介面來指定其屬性。這項功能的主要使用案例是描述物件的工廠。下一個區段會提供一個範例。
以下兩個介面可用於支援其實例從 JSON 轉換和轉換成 JSON 的類別
// Converting JSON to instances
interface JsonStatic {fromJson(json: any): JsonInstance;
}
// Converting instances to JSON
interface JsonInstance {toJson(): any;
}
我們在以下程式碼中使用這些介面
class Person implements JsonInstance {fromJson(json: any): Person {
static if (typeof json !== 'string') {
new TypeError(json);
throw
}new Person(json);
return
}: string;
nameconstructor(name: string) {
.name = name;
this
}toJson(): any {
.name;
return this
} }
以下是我們可以立即檢查類別 Person
(作為物件)是否實作介面 JsonStatic
的方式
// Assign the class to a type-annotated variable
: JsonStatic = Person; const personImplementsJsonStatic
以下進行檢查的方式看起來似乎不錯
: JsonStatic = class implements JsonInstance {
const Person// ···
; }
不過,這並不切實際
new
呼叫 Person
,因為 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;
(): any): any;
(value
/** 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. */
: Function;
constructor
/** Returns a string representation of an object. */
toString(): string;
}
名稱 Object
在兩個不同的 語言層級 中使用兩次
考慮下列類別
class Color {: string;
nameconstructor(name: string) {
.name = name;
this
} }
這個類別定義建立兩件事。
首先,一個名為 Color
的建構函式(可透過 new
呼叫)
.equal(
assert, 'function') typeof Color
其次,一個名為 Color
的介面,與 Color
的執行個體相符
: Color = new Color('green'); const green
以下是 Color
確實為介面的證明
interface RgbColor extends Color {: [number, number, number];
rgbValue }
不過有一個陷阱:將 Color
用作靜態類型並非非常嚴格的檢查
class Color {: string;
nameconstructor(name: string) {
.name = name;
this
}
}
class Person {: string;
nameconstructor(name: string) {
.name = name;
this
}
}
: Person = new Person('Jane');
const person: Color = person; // (A) const color
為什麼 TypeScript 沒有在 A 行抱怨?這是因為結構化類型:Person
和 Color
的執行個體具有相同的結構,因此在靜態上相容。
我們可以透過新增私有屬性,讓這兩組物件不相容
class Color {: string;
name= true;
private branded constructor(name: string) {
.name = name;
this
}
}
class Person {: string;
name= true;
private branded constructor(name: string) {
.name = name;
this
}
}
: Person = new Person('Jane');
const person
// @ts-expect-error: Type 'Person' is not assignable to type 'Color'.
// Types have separate declarations of a private property
// 'branded'. (2322)
: Color = person; const color
私有屬性在此情況下會關閉結構化類型。