名目型付けと構造型付け
静的型チェッカーは、型を他の型と比較する際(ある型が別の型のサブタイプであるかどうかをチェックする場合など)に、型の名前(名目型付け)または構造(構造型付け)のいずれかを使用できます。
名目型付け
C++、Java、Swiftなどの言語は、主に名目型システムを備えています。
// Pseudo code: nominal system
class Foo { method(input: string) { /* ... */ } }
class Bar { method(input: string) { /* ... */ } }
let foo: Foo = new Bar(); // Error!
この擬似コードの例では、両方のクラスに同じ名前と型のメソッドがあるにもかかわらず、名目型システムはエラーになります。これは、クラスの名前(および宣言場所)が異なるためです。
構造型付け
GoやElmなどの言語は、主に構造型システムを備えています。
// Pseudo code: structural system
class Foo { method(input: string) { /* ... */ } }
class Bar { method(input: string) { /* ... */ } }
let foo: Foo = new Bar(); // Works!
この擬似コードの例では、両方のクラスに同じ名前と型のメソッドとフィールドがあるため、構造型システムではBar
をFoo
として使用できます。
ただし、クラスの形状が異なる場合は、構造システムでエラーが発生します。
// Pseudo code
class Foo { method(input: string) { /* ... */ } }
class Bar { method(input: number) { /* ... */ } }
let foo: Foo = new Bar(); // Error!
クラスの名目型付けと構造型付けの両方を説明しましたが、オブジェクトや関数など、名目的または構造的に比較できる他の複雑な型もあります。さらに、型システムには、構造システムと名目システムの両方の側面がある場合があります。
Flowの場合
Flowは、オブジェクトと関数には構造型付けを使用しますが、クラスには名目型付けを使用します。
関数は構造的に型付けされます
関数型を関数と比較する場合、それが有効と見なされるためには同じ構造を持っている必要があります。
1type FuncType = (input: string) => void;2function func(input: string) { /* ... */ }3let test: FuncType = func; // Works!
オブジェクトは構造的に型付けされます
オブジェクト型をオブジェクトと比較する場合、それが有効と見なされるためには同じ構造を持っている必要があります。
1type ObjType = {property: string};2let obj = {property: "value"};3let test: ObjType = obj; // Works
クラスは名目的に型付けされます
同じ構造を持つ2つのクラスがある場合でも、Flowはクラスに名目型付けを使用するため、それらは同等とは見なされません。
1class Foo { method(input: string) { /* ... */ } }2class Bar { method(input: string) { /* ... */ } }3let test: Foo = new Bar(); // Error!
3:17-3:25: Cannot assign `new Bar()` to `test` because `Bar` [1] is incompatible with `Foo` [2]. [incompatible-type]
クラスを構造的に使用したい場合は、インターフェースを使用できます。
1interface Interface {2 method(value: string): void;3};4
5class Foo { method(input: string) { /* ... */ } }6class Bar { method(input: string) { /* ... */ } }7
8let test1: Interface = new Foo(); // Works9let test2: Interface = new Bar(); // Works
不透明型
不透明型を使用すると、以前に構造的に型付けされたエイリアスを(定義されているファイルの外で)名目型に変換できます。
1// A.js2export type MyTypeAlias = string;3export opaque type MyOpaqueType = string;4
5const x: MyTypeAlias = "hi"; // Works6const y: MyOpaqueType = "hi"; // Works
別のファイル
// B.js
import type {MyTypeAlias, MyOpaqueType} from "A.js";
const x: MyTypeAlias = "hi"; // Works
const y: MyOpaqueType = "hi"; // Error! `MyOpaqueType` is not interchangable with `string`
// ^^^^ Cannot assign "hi" to y because string is incompatible with MyOpaqueType
Flow Enum
Flow Enumでは、同じ値を持つが異なるEnumに属するEnumメンバーを相互に使用することはできません。
1enum A {2 X = "x",3}4enum B {5 X = "x",6}7
8const a: A = B.X; // Error!
8:14-8:16: Cannot assign `B.X` to `a` because `B` [1] is incompatible with `A` [2]. [incompatible-type]