Structural typing仅真正适用于比较对象或类对象类型. 或者至少是比较两种类型的 struct 或形状的部分.这种比较需要在某个地方"触底",这几乎是不言而喻的,否则您可能能够得出几乎每对类型都共享一个 struct (例如,如果"a"
和"b"
由于都是string
而具有相同的 struct ,那么{a: Date}
和{b: Date}
将具有相同的 struct ,因为键"a"
和"b"
是相同的,并且很快您最终得出结论,类型景观的大片实际上是一种类型).
比较两种类型的形状本质上是一个迭代过程,要终止该过程,您需要一些基本情况.基本词充当此类基本 case .这些基本情况比 struct 类型更接近nominal typing. 如果两种名义类型在不同的地方声明,则被认为是不同的.
TypScript中的非空primitives被视为与其自动装箱包装器对象相对应的interfaces的子类型. 这些接口具有与基元关联的方法和属性,例如toUpperCase()
代表String
,toFixed()
代表Number
. 这些基元是其包装器类型的proper个子类型,这意味着,例如,string
(基元类型)被认为可分配给String
(包装器对象类型)but not vice versa:
declare let s: string;
declare let S: String;
S = s; // okay
s = S; // error
此外,literal types是其相关原始类型的适当子类型.例如,"a"
被认为可分配给string
,但反之亦然:
declare let a: "a";
declare let s: string;
s = a; // okay
a = s; // error
这为类型层次 struct 增加了一点深度,因此"a"
将可分配给{length: number}
,因为"a"
可分配给string
,而string
可分配给String
,而String
在 struct 上可分配给{length: number}
:
let lengthable: { length: number } = a; // okay
因此,您确实可以将基元的 struct 与其他类型进行比较,但单独 struct 并不能解释类型的差异.
如果纯粹从 struct 上思考有帮助,那么您可以通过想象类型具有区分它们的"幻影"属性来做到这一点.您可能会认为string
看起来像String & {[str]: true}
,而str
是某种独特的符号.事实上,这样的技术实际上在实践中使用到了emulate nominal typing;参见Intersection of primitive and object types with Typescript.
Playground link to code