名义类型和结构类型
静态类型检查器可以使用类型名称(名义类型)或类型结构(结构类型)来比较类型(例如,检查一个类型是否是另一个类型的子类型)。
名义类型
像 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
类是名义类型的
当你有两个结构相同的类时,它们仍然不被视为等效,因为 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 枚举
Flow 枚举不允许具有相同值的枚举成员(但属于不同的枚举)可以互换使用。
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]