无比打字与拼英打字
标称和结构分型 (Nominal & structural typing)
Type systems are typically categorized as either structural or nominal. Languages like Java and Scala have primarily nominal type systems, whereas a language like Typescript has a structural type system. Let’s take a brief look at both systems.
类型系统通常分为结构或名义上。 诸如Java和Scala之类的语言主要具有名义类型系统,而诸如Typescript之类的语言则具有结构类型系统。 让我们简要介绍一下这两个系统。
标称打字 (Nominal Typing)
In a nominal typing system, type compatibility is checked using the name of the types. If they do not have the same name, then they are not compatible; end of story. If Typescript had a nominal typing system the type check for the last line would fail:
在名义类型系统中,使用类型名称检查类型兼容性。 如果它们的名称不同,则它们不兼容;否则,它们不兼容。 故事结局。 如果 Typescript具有标称键入系统,则最后一行的类型检查将失败:
结构类型 (Structural typing)
Typescript uses structural typing to decide whether two types are compatible with one another or not. What do we mean by structural typing? Well, let’s consider the following code snippet:
Typescript使用结构化类型来确定两种类型是否彼此兼容。 我们所说的结构化打字是什么意思? 好吧,让我们考虑以下代码片段:
To determine whether the type of the constant color
(RGBA
) is compatible with the type of serializeColor
’s parameter x
(RGB
) the type system must verify that each member of RGB
has a corresponding compatible member in RGBA
. In this case, RGB
has a single member color
for which RGBA
has a corresponding member with the same type — [number, number, number]
— and so it passes the type check. Notice how the type system ignores the additional members that exist on RGBA
(alpha).
为了确定常量color
( RGBA
)的类型是否与serializeColor
的参数x
( RGB
)的类型兼容,类型系统必须验证RGB
每个成员在RGBA
都有相应的兼容成员。 在这种情况下, RGB
具有单一成员color
,而RGBA
具有对应的成员具有相同的类型- [number, number, number]
,因此它通过类型检查。 请注意,类型系统如何忽略RGBA
(alpha)上存在的其他成员。
TS中的名义类型 (Nominal types in TS)
Even though Typescript has a primarily structural type system, we can still implement some form of nominal types if we follow a standard pattern when defining types so that the type system will always treat them as distinct types. This structure will act as a unique identifier and descriptor of that type. Such an implementation might look something like this:
即使Typescript具有主要的结构类型系统,但如果在定义类型时遵循标准模式,我们仍然可以实现某种形式的名义类型,以便类型系统始终将它们视为不同的类型。 该结构将充当该类型的唯一标识符和描述符。 这样的实现可能看起来像这样:
the type
member represents the name of the type, while the data
member describes its type of data. Now if I’d create a value of type RGB
I’d have to do it in the following way:
type
成员表示type
的名称,而data
成员描述其数据的类型。 现在,如果要创建RGB
类型的值,则必须采用以下方式:
Of course, writing this structure for every single nominal-typed value would be tedious work. This is where value constructors come to the rescue. A value constructor is a function that facilitates constructing values of a specific type without all the boilerplate and noise. Let’s create one for RGB
:
当然,为每个单个标称值的值编写此结构都是繁琐的工作。 这就是价值构造函数来解救的地方。 值构造函数是一种有助于构造特定类型的值而没有所有样板和噪声的函数。 让我们为RGB
创建一个:
And now we can create a value of type RGB
with less verbose code:
现在,我们可以使用较少的冗长代码创建RGB
类型的值:
Now that we’ve introduced value constructors, let’s incorporate one in our nominal type interface:
现在,我们介绍了值构造函数,让我们在名义类型接口中加入一个:
At first sight, you might feel a bit uncertain of what value the valueConstructor
member brings to the type system, but it does actually serve two purposes:
乍一看,您可能会不确定valueConstructor
成员为类型系统带来什么价值,但实际上它有两个作用:
Together with
type
anddata
, they lay out a complete description of a type: the name of the type, the shape of its data, and how to construct it.他们与
type
和data
一起,对type
进行了完整的描述:类型的名称,数据的形状以及构造方法。- You can build type guards based on value-constructors as we shall see when we discuss subtypes. 正如我们在讨论子类型时将看到的那样,您可以基于值构造函数构建类型保护。
But before moving on to discuss subtypes, there is a small improvement we can implement to our nominal type interface. Since all our nominal types share the same properties, we can abstract these away in a generic NominalType
interface as follows:
但是在继续讨论子类型之前,我们可以对名义类型接口进行一些小的改进。 由于我们所有的标称类型都共享相同的属性,因此可以在通用的NominalType
接口中将它们抽象化,如下所示:
One tip that I’d like to share in case you are implementing nominal types in a package that you will publish— say on npm — is to add a level of scoping to your interface to avoid conflicts with other libraries, you can do that like this:
如果您要在要发布的包中实现名义类型(例如在npm上),我想分享的一个技巧是在您的界面中添加一定范围的范围以避免与其他库冲突,您可以这样做这个:
TS中的标称亚型 (Nominal subtypes in TS)
There are a couple of patterns that I follow when modeling type-subtype relationships, depending on a number of factors, including but not limited to:
在对类型-子类型关系进行建模时,我会遵循几种模式,具体取决于许多因素,包括但不限于:
How VS’s code tooltip represents that type — Sometimes the tooltip skips the hierarchy of types when using the
type
keyword and so I might use theinterface
keyword instead oftype
to solve this issue — .VS的代码工具提示如何表示该类型-有时,在使用
type
关键字时,该工具提示会跳过类型的层次结构,因此我可以使用interface
关键字而不是type
来解决此问题-。- Do I need the type just to eliminate the repetition of writing the same tagged union over and over again? Am I exposing more functions that operate on subtypes than the parent type itself? 我是否需要这种类型只是为了消除一次又一次写相同标签的联合的重复? 与父类型本身相比,我是否公开了更多对子类型进行操作的函数?
The first pattern is where the type itself is modeled as a union type and its subtypes as independent nominal types. Let’s say we want to implement a color type, now there are many “types” of colors, hex, rgb, rgba, hsl, hsla, color names, etc….
第一种模式是将类型本身建模为联合类型,并将其子类型建模为独立的标称类型。 假设我们要实现一种颜色类型,现在有许多颜色的“类型”,例如十六进制,rgb,rgba,hsl,hsla,颜色名称等。
For the sake of simplicity, we will only support two types of colors: rgb and rgba. We can model this the following way:
为了简单起见,我们将仅支持两种颜色: rgb和rgba 。 我们可以通过以下方式对此进行建模:
For this pattern, we can create type guards based on the name of the type as follows:
对于此模式,我们可以根据类型名称创建类型防护,如下所示:
Another pattern is using an interface with a type parameter to specify the subtype, like the following:
另一种模式是使用带有类型参数的接口来指定子类型,如下所示:
Here, we use Color<'RGB'>
and Color<'RGBA'>
to represent subtypes RGB
and RGBA
respectively, and use Color
without a type parameter to accept both. Using this pattern, we can implement type guards based on the value of valueConstructor
instead of name
在这里,我们使用Color<'RGB'>
和Color<'RGBA'>
分别表示RGB
和RGBA
子类型,并使用不带类型参数的Color
接受两者。 使用此模式,我们可以基于valueConstructor
的值而不是name
来实现类型保护
Thanks for reading. I hope this was helpful. If you have any thoughts or questions, please feel free to leave a response, it might always teach someone something new :)
谢谢阅读。 我希望这可以帮到你。 如果您有任何想法或问题,请随时回复,它可能总是会教给别人一些新的东西:)
If it doesn’t make sense, keep remodeling it until it does
如果没有意义,请继续对其进行重塑,直到它有意义为止
翻译自: https://levelup.gitconnected.com/nominal-typing-in-typescript-c712e7116006
无比打字与拼英打字