メインコンテンツへスキップ

インターフェース

Flowにおけるクラスは、名義的型付けです。これは、2つの別々のクラスが、まったく同じプロパティとメソッドを持っている場合でも、一方を他方の代わりに使用できないことを意味します。

1class Foo {2  serialize(): string { return '[Foo]'; }3}4
5class Bar {6  serialize(): string { return '[Bar]'; }7}8
9const foo: Foo = new Bar(); // Error!
9:18-9:26: Cannot assign `new Bar()` to `foo` because `Bar` [1] is incompatible with `Foo` [2]. [incompatible-type]

代わりに、期待するクラスの構造を宣言するために`interface`を使用できます。

1interface Serializable {2  serialize(): string;3}4
5class Foo {6  serialize(): string { return '[Foo]'; }7}8
9class Bar {10  serialize(): string { return '[Bar]'; }11}12
13const foo: Serializable = new Foo(); // Works!14const bar: Serializable = new Bar(); // Works!

匿名インターフェースも宣言できます。

1class Foo {2  a: number;3}4
5function getNumber(o: interface {a: number}): number {6  return o.a;7}8
9getNumber(new Foo()); // Works!

また、`implements`を使用して、クラスがインターフェースに一致するようにFlowに指示することもできます。これにより、クラスを編集する際に互換性のない変更を加えるのを防ぎます。

1interface Serializable {2  serialize(): string;3}4
5class Foo implements Serializable {6  serialize(): string { return '[Foo]'; } // Works!7}8
9class Bar implements Serializable {10  serialize(): number { return 42; } // Error!
11}
10:16-10:21: Cannot implement `Serializable` [1] with `Bar` because number [2] is incompatible with string [3] in the return value of property `serialize`. [incompatible-type]

複数のインターフェースで`implements`を使用することもできます。

class Foo implements Bar, Baz {
// ...
}

インターフェースは、オブジェクト型がオブジェクトのみを記述できるのに対し、インスタンスとオブジェクトの両方を記述できます。

1class Foo {2  a: number;3}4const foo = new Foo();5const o: {a: number} = {a: 1};6
7interface MyInterface {8  a: number;9}10
11function acceptsMyInterface(x: MyInterface) { /* ... */ }12acceptsMyInterface(o); // Works!13acceptsMyInterface(foo); // Works!14
15function acceptsObj(x: {a: number, ...}) { /* ... */ }16acceptsObj(o); // Works!17acceptsObj(foo); // Error!
17:12-17:14: Cannot call `acceptsObj` with `foo` bound to `x` because `Foo` [1] is not a subtype of object type [2]. Class instances are not subtypes of object types; consider rewriting object type [2] as an interface. [class-object-subtyping]

オブジェクトとは異なり、インターフェースは厳密な型にすることはできません。常に、他の未知のプロパティを持つ可能性があるためです。

インターフェース構文

インターフェースは、キーワード`interface`、その名前、そして型定義の本体を含むブロックを使用して作成されます。

1interface MyInterface {2  // ...3}

ブロックの構文は、オブジェクト型の構文と一致します。

インターフェースメソッド

クラスメソッドと同じ構文に従って、インターフェースにメソッドを追加できます。提供する`this`パラメータにも、クラスメソッドと同じ制限が適用されます。

1interface MyInterface {2  method(value: string): number;3}

また、クラスメソッドと同様に、インターフェースメソッドも、定義されたインターフェースにバインドされたままにする必要があります。

異なる型シグネチャで同じメソッド名を複数回宣言することで、オーバーロードされたメソッドを定義できます。

1interface MyInterface {2  method(value: string): string;3  method(value: boolean): boolean;4}5
6function func(a: MyInterface) {7  const x: string = a.method('hi'); // Works!8  const y: boolean = a.method(true); // Works!9
10  const z: boolean = a.method('hi'); // Error!
11}
10:22-10:35: Cannot assign `a.method(...)` to `z` because string [1] is incompatible with boolean [2]. [incompatible-type]

インターフェースプロパティ

クラスプロパティと同じ構文に従って、インターフェースにプロパティを追加できます。

1interface MyInterface {2  property: string;3}

インターフェースプロパティはオプションにすることもできます。

1interface MyInterface {2  property?: string;3}

マップとしてのインターフェース

オブジェクトと同様に、インデックス付きプロパティを作成できます。

1interface MyInterface {2  [key: string]: number;3}

インターフェースジェネリクス

インターフェースは、独自のジェネリクスを持つこともできます。

1interface MyInterface<A, B, C> {2  property: A;3  method(val: B): C;4}

インターフェースジェネリクスはパラメーター化されます。インターフェースを使用する際には、各ジェネリクスにパラメーターを渡す必要があります。

1interface MyInterface<A, B, C> {2  foo: A;3  bar: B;4  baz: C;5}6
7const val: MyInterface<number, boolean, string> = {8  foo: 1,9  bar: true,10  baz: 'three',11};

インターフェースプロパティの分散 (読み取り専用と書き込み専用)

インターフェースプロパティは、デフォルトでは不変です。しかし、修飾子を追加して、共変 (読み取り専用) または反変 (書き込み専用) にすることができます。

1interface MyInterface {2  +covariant: number;     // read-only3  -contravariant: number; // write-only4}

インターフェースの共変 (読み取り専用) プロパティ

プロパティ名の前にプラス記号`+`を追加することで、プロパティを共変にすることができます。

1interface MyInterface {2  +readOnly: number | string;3}

これにより、そのプロパティの代わりに、より具体的な型を渡すことができます。

1interface Invariant {2  property: number | string;3}4interface Covariant {5  +readOnly: number | string;6}7
8const x: {property: number} = {property: 42};9const y: {readOnly: number} = {readOnly: 42};10
11const value1: Invariant = x; // Error!
12const value2: Covariant = y; // Works
11:27-11:27: Cannot assign `x` to `value1` because string [1] is incompatible with number [2] in property `property`. This property is invariantly typed. See https://flow.dokyumento.jp/en/docs/faq/#why-cant-i-pass-a-string-to-a-function-that-takes-a-string-number. [incompatible-type]

共変の仕組みのため、共変プロパティは使用時には読み取り専用にもなります。これは通常のプロパティよりも有用な場合があります。

1interface Invariant {2  property: number | string;3}4interface Covariant {5  +readOnly: number | string;6}7
8function func1(value: Invariant) {9  value.property;        // Works!10  value.property = 3.14; // Works!11}12
13function func2(value: Covariant) {14  value.readOnly;        // Works!15  value.readOnly = 3.14; // Error!
16}
15:9-15:16: Cannot assign `3.14` to `value.readOnly` because property `readOnly` is not writable. [cannot-write]

インターフェースの反変 (書き込み専用) プロパティ

プロパティ名の前にマイナス記号`-`を追加することで、プロパティを反変にすることができます。

1interface InterfaceName {2  -writeOnly: number;3}

これにより、そのプロパティの代わりに、より具体的な型を渡すことができます。

1interface Invariant {2  property: number;3}4interface Contravariant {5  -writeOnly: number;6}7
8const numberOrString = Math.random() > 0.5 ? 42 : 'forty-two';9
10const value1: Invariant     = {property: numberOrString};  // Error!
11const value2: Contravariant = {writeOnly: numberOrString}; // Works!
10:42-10:55: Cannot assign object literal to `value1` because string [1] is incompatible with number [2] in property `property`. [incompatible-type]

反変の仕組みのため、反変プロパティは使用時には書き込み専用にもなります。これは通常のプロパティよりも有用な場合があります。

1interface Invariant {2  property: number;3}4interface Contravariant {5  -writeOnly: number;6}7
8function func1(value: Invariant) {9  value.property;        // Works!10  value.property = 3.14; // Works!11}12
13function func2(value: Contravariant) {14  value.writeOnly;        // Error!
15 value.writeOnly = 3.14; // Works!16}
14:9-14:17: Cannot get `value.writeOnly` because property `writeOnly` is not readable. [cannot-read]