无比打字与拼英打字
It’s not uncommon for people to think of Object-Oriented Programming and Functional Programming as mutually exclusive, and understandably so; any discussion involving the two turns quite often into a debate where rivalry is the central theme. But as it turns out, in some contexts, we can benefit from employing disciplines from both paradigms to get the best of two worlds. Ad-hoc Polymorphism in Typescript is one such context.
人们想到面向对象的编程并不少见 和函数式编程 可以理解为互斥的; 任何涉及这两个方面的讨论都经常变成以竞争为中心的辩论。 但是事实证明,在某些情况下,我们可以从两种范式中采用学科来受益,以充分利用两个世界。 打字稿中的临时多态性 就是这样一种背景。
场景 (The scenario)
Consider the case where we’re developing an inline-styling library. The library has different types that correspond to different CSS data types, such as:
考虑一下我们正在开发内联样式库的情况。 该库具有对应于不同CSS数据类型的不同类型,例如:
- RGB RGB
- RGBA RGBA
- HSL 高速钢
- HSLAHSLA
- HEX 十六进制
- Length长度
- Percentage百分比
- Decibel分贝
- Time时间
- Transformation转型
For the sake of simplicity, our interface for these inline-style objects will only support one property: The color
property.
为了简单起见,我们用于这些内联样式对象的接口将仅支持一种属性: color
属性。

As demonstrated in the photo above, the value of the color property in our interface has a tagged-union type, which consists of a stringLiteral
type and five other custom types represented as Javascript objects and modeled after the nominal-typing model described in my previous article.
如上图所示,我们界面中的color属性值具有一个标记联合类型,该类型由stringLiteral
类型和其他五个自定义类型(表示为Javascript对象)组成,并按照我之前描述的标称类型模型进行建模文章。
Now, within the boundaries of the library’s code, these types are well understood, and their values can be manipulated accordingly, but outside these boundaries — say in React, for example — they bare no meaning. For React to utilize these values for styling, we need to serialize them into either string
or number
values.
现在,在库代码的边界内,这些类型已得到很好的理解,并且它们的值可以进行相应的操作,但是在这些边界之外(例如在React中),它们毫无意义。 为了使React利用这些值进行样式设置,我们需要将它们序列化为string
或number
值。
问题 (The problem)
Based on our scenario, we need a function that serializes a color value into a string
value. One way to do it can be this:
根据我们的方案,我们需要一个将颜色值序列化为string
值的函数。 一种方法是:

Nothing fancy here. We create small functions, each responsible for serializing a single subtype (a subtype of color) and then creating a function that checks the type of the passed argument (using type guards) and calls the function associated with that type. This approach results in excess code and an increased number of imports(in this case, we uses five imports).
这里没什么好看的。 我们创建了一些小的函数,每个函数负责序列化一个子类型(颜色的子类型),然后创建一个函数来检查传递的参数的类型(使用类型保护)并调用与该类型关联的函数。 这种方法导致代码过多和导入数量增加(在这种情况下,我们使用五个导入)。
In Haskell, you’d be using ad-hoc polymorphism to deal with this scenario. You’d have different implementations under the same name associated with different types, and Haskell will automatically pick the appropriate implementation for you depending on the type of the argument. If Typescript had it, it would “kinda” look something like this (without the error, of course):
在Haskell中,您将使用即席多态处理这种情况。 您将使用与不同类型关联的相同名称使用不同的实现,Haskell会根据参数的类型自动为您选择合适的实现。 如果Typescript拥有它,它将“有点”看起来像这样(当然没有错误):

As you can see, TS’s compiler is complaining about “duplicate function implementation”. This error shows up because you can have multiple signatures under the same name, but only one implementation is allowed. This implementation is responsible for finding the appropriate function to call. So to fix the error, we need to do the following:
如您所见,TS的编译器抱怨“功能重复”。 出现此错误的原因是,您可以在同一名称下使用多个签名,但只允许一个实现。 此实现负责查找要调用的适当函数。 因此,要纠正该错误,我们需要执行以下操作:

Absolutely worthless! We’ve ended up in a similar situation as before, only worse, with more code. Typescript calls this function overloading. It can be handy in many cases, but not in this one.
绝对一文不值! 我们最终陷入了与以前类似的情况,但更糟糕的是,有了更多的代码。 Typescript将此函数称为重载。 在许多情况下,它可能很方便,但在这种情况下却不是。
In Typescript, you can have many signatures under the same name, but only one implementation is allowed to associate with that name
在Typescript中,同一名称下可以有多个签名,但是只允许一个实现与该名称关联
Function overloading in TS didn’t work as expected, you feel sad and betrayed, the voice inside your head is urging you to reach out to the dark side, where all the poor objects scream in pain caused by mutations and method binding. But then, just right before you make your first step into the OOP world, I — your guardian angel — show up and stop you, offering you a middle ground, a safe place where your functions can thrive together with your objects in a pure atmosphere.
TS中的功能超载未能按预期工作,您感到难过和被出卖,您内心的声音敦促您伸向黑暗的一面,那里所有可怜的物体都因突变和方法绑定而痛苦地尖叫。 但是然后,就在您踏入OOP世界的第一步之前,我-您的守护天使-出现并阻止了您,为您提供了一个中间立场,一个安全的地方,您的功能可以在纯净的氛围中与您的对象一起蓬勃发展。

解决方案(The solution)
My go-to approach to this problem is to keep a reference of the function inside the “typed” object, stolen right from the OOP world. Don’t panic, there is no state involved in this approach, and the function is still referentially transparent. This reference is merely there to give easy and quick access to the appropriate function and eliminate code redundancy. We can do that by adding a property whose value is a reference of the function, something like this:
我要解决的这个问题的方法是将函数的引用保留在OOP世界中被盗的“类型化”对象内。 不要惊慌,也没有参与这种做法状态,而且功能还指称transparen吨。 该参考仅用于使您轻松快速地访问适当的功能并消除代码冗余。 我们可以通过添加一个属性值来实现此目的,该属性的值是函数的引用,如下所示:

You can ignore the properties type
and data
. Thergb
function is just a factory function that creates values of type RGB
. The resulting object will have the property serialize
that points to the function serializeRGB
. Following this pattern with our other custom types, we can now refactor our serializeColor
function to the following form:
您可以忽略属性type
和data
。 rgb
函数只是一个工厂函数,它创建RGB
类型的值。 得到的对象将有物业serialize
指向功能serializeRGB
。 遵循此模式以及其他自定义类型,我们现在可以将serializeColor
函数重构为以下形式:

Each type is responsible for pointing to its own implementation of the serialization behavior, no need for imports, or manual type checking.
每种类型都有责任指向自己的序列化行为实现,无需导入,也无需手动进行类型检查。
I hope you agree with me that this is way neater than the previous version. If you don’t, that’s fine. We all have the right to have our own opinions, just leave a comment, and I’ll let Liam Neeson find you and convince you of mine.
我希望您同意我的看法,这比以前的版本更加整洁。 如果您不这样做,那很好。 我们所有人都有发表意见的权利,请发表评论,我会让利亚姆·尼森(Liam Neeson)找到您并说服您。
We can keep references to functions inside objects to associate a particular type with an overloaded behavior without breaking the purity of our functional code.
我们可以保留对对象内部函数的引用,以使特定类型与重载行为相关联,而不会破坏我们的功能代码的纯度。
回顾 (Recap)
- It’s possible to bring some disciplines from OOP into our functional code without breaking its purity.可以将OOP的某些学科带入我们的功能代码,而不会破坏其纯度。
- Using references is an excellent way to implement ad-hoc-like polymorphism in TS. 使用引用是在TS中实现类似ad-hoc的多态性的绝佳方法。
The scenario that I’ve used in this article is actually a simplified version of one that I found myself dealing with in my project, Rosebox, a styling library I’ve written in Typescript. Check it out.
我在本文中使用的场景实际上是我在自己的项目Rosebox中处理过的场景的简化版本, Rosebox是我用Typescript编写的样式库。 看看这个。
翻译自: https://levelup.gitconnected.com/polymorphism-in-typescript-59fad77f8cd7
无比打字与拼英打字