名义型和结构型
静态类型检查器在将类型与其他类型进行比较时(例如检查一个类型是否是另一个类型的 亚型 时),可以使用类型的名称(名义类型)或结构(结构类型)。
¥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!