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