跳至主要内容

名义类型和结构类型

静态类型检查器可以使用类型名称(名义类型)或类型结构(结构类型)来比较类型(例如,检查一个类型是否是另一个类型的子类型)。

名义类型

像 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]