Skip to main content

名义型和结构型

静态类型检查器在将类型与其他类型进行比较时(例如检查一个类型是否是另一个类型的 亚型 时),可以使用类型的名称(名义类型)或结构(结构类型)。

¥A static type checker can use either the name (nominal typing) or the structure (structural typing) of types when comparing them against other types (like when checking if one is a subtype of another).

标称型号

¥Nominal typing

C++、Java 和 Swift 等语言主要具有名义类型系统。

¥Languages like C++, Java, and Swift have primarily nominal type systems.

// Pseudo code: nominal system
class Foo { method(input: string) { /* ... */ } }
class Bar { method(input: string) { /* ... */ } }

let foo: Foo = new Bar(); // Error!

在此伪代码示例中,即使两个类具有相同名称和类型的方法,名义类型系统也会出错。这是因为类的名称(和声明位置)不同。

¥In this pseudo-code example, the nominal type system errors even though both classes have a method of the same name and type. This is because the name (and declaration location) of the classes is different.

结构类型

¥Structural typing

像 Go 和 Elm 这样的语言主要有结构类型系统。

¥Languages like Go and Elm have primarily structural type systems.

// Pseudo code: structural system
class Foo { method(input: string) { /* ... */ } }
class Bar { method(input: string) { /* ... */ } }

let foo: Foo = new Bar(); // Works!

在此伪代码示例中,结构类型系统允许将 Bar 用作 Foo,因为两个类都具有相同名称和类型的方法和字段。

¥In this pseudo-code example, the structural type system allows a Bar to be used as a Foo, since both classes have methods and fields of the same name and type.

然而,如果类的形状不同,那么结构系统就会产生错误:

¥If the shape of the classes differ however, then a structural system would produce an error:

// Pseudo code
class Foo { method(input: string) { /* ... */ } }
class Bar { method(input: number) { /* ... */ } }

let foo: Foo = new Bar(); // Error!

我们已经演示了类的名义类型和结构类型,但还有其他复杂类型,例如对象和函数,它们也可以在名义上或结构上进行比较。另外,类型系统可以具有结构系统和标称系统的方面。

¥We've demonstrated both nominal and structural typing of classes, but there are also other complex types like objects and functions which can also be either nominally or structurally compared. Additionally, a type system may have aspects of both structural and nominal systems.

在 Flow 中

¥In Flow

Flow 对对象和函数使用结构类型,但对类使用名义类型。

¥Flow uses structural typing for objects and functions, but nominal typing for classes.

函数的结构类型为

¥Functions are structurally typed

函数类型 与函数进行比较时,它必须具有相同的结构才能被视为有效。

¥When comparing a function type with a function it must have the same structure in order to be considered valid.

1type FuncType = (input: string) => void;2function func(input: string) { /* ... */ }3let test: FuncType = func; // Works!

对象的结构类型为

¥Objects are structurally typed

对象类型 与对象进行比较时,它必须具有相同的结构才能被视为有效。

¥When comparing an object type with an object it must have the same structure in order to be considered valid.

1type ObjType = {property: string};2let obj = {property: "value"};3let test: ObjType = obj; // Works

类的名义类型为

¥Classes are nominally typed

当你有两个具有相同结构的 时,它们仍然不被视为等效,因为 Flow 对类使用名义类型。

¥When you have two classes with the same structure, they still are not considered equivalent because Flow uses nominal typing for classes.

1class Foo { method(input: string) { /* ... */ } }2class Bar { method(input: string) { /* ... */ } }3let test: Foo = new Bar(); // Error!

如果你想在结构上使用一个类,你可以使用 界面 来做到这一点:

¥If you wanted to use a class structurally you could do that using an interface:

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

不透明类型

¥Opaque types

你可以使用 不透明类型 将先前的结构类型别名转换为名义别名(在定义它的文件之外)。

¥You can use opaque types to turn a previously structurally typed alias into a nominal one (outside of the file that it is defined).

1// A.js2export type MyTypeAlias = string;3export opaque type MyOpaqueType = string;4
5const x: MyTypeAlias = "hi"; // Works6const y: MyOpaqueType = "hi"; // Works

在不同的文件中:

¥In a different file:

// 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 Enums

Flow 枚举 不允许具有相同值但属于不同枚举的枚举成员互换使用。

¥Flow Enums do not allow enum members with the same value, but which belong to different enums, to be used interchangeably.

1enum A {2  X = "x",3}4enum B {5  X = "x",6}7
8const a: A = B.X; // Error!