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
のランタイム構造を示しています。この図には、オブジェクトの2つのプロトタイプチェーンがあります。
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
など)を区別しません。クラス自体もオブジェクト(関数)です。したがって、インターフェースを使用してそのプロパティを指定できます。ここでの主なユースケースは、オブジェクトのファクトリを記述することです。次のセクションで例を示します。
次の2つのインターフェースは、インスタンスの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// ···
; }
ただし、実際には機能しません。
JsonStatic
にはコンストラクトシグネチャがないため、Person
をnew
で呼び出すことはできません。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
は、2つの異なる言語レベルで2回使用されます。
次のクラスを考えてみましょう。
class Color {: string;
nameconstructor(name: string) {
.name = name;
this
} }
このクラス定義は、次の2つのものを作成します。
まず、Color
という名前のコンストラクター関数(new
で呼び出すことができます)。
.equal(
assert, 'function') typeof Color
次に、Color
のインスタンスに一致するColor
という名前のインターフェース。
: Color = new Color('green'); const green
これが、Color
が実際にインターフェースである証拠です。
interface RgbColor extends Color {: [number, number, number];
rgbValue }
ただし、1つの落とし穴があります。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
のインスタンスは同じ構造を持ち、したがって静的に互換性があります。
プライベートプロパティを追加することにより、オブジェクトの2つのグループを非互換にすることができます。
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
この場合、プライベートプロパティは構造的型付けをオフにします。