打字稿
价值类型

通过manbetx官方网站多少

类型脚本:值类型

这种技术是价值客体我已经用了很多年了,用不同的语言。要点是你创建了一个装箱价值,它封装了价值的业务规则,不能是基元值的属性,像一根绳子。

假设我有一个类,允许向用户发送电子邮件:发送电子邮件(电子邮件,主题,课文).所有三个参数都是字符串:

class mailer/***@param string email*@param string subject*@param string text*/public async sendmail(email,主题,文本)/…}

现在很容易用这个方法编程来构造正确的调用,但参数无效:

//email参数不是email.sendmail('john doe',“你好”“文本……”

也很容易改变论点的顺序,因为它们都打成一串

//email参数不是firstm.sendmail('hello',“文本…”'john@example.com')

如果我们转而电子邮件到一个值对象中,我们可以减少这些错误:

类emailValue构造函数(email)if(!isvalidemail(email))抛出新类型错误(“不是电子邮件:”+email)this.email=email;}

现在,第一个例子不再可能:

//throws类型错误m.sendmail(new emailvalue('john doe'),“你好”“文本……”

第二个例子不太可能发生,因为在IDE中更容易发现第一个参数是对象,不是字符串,而且在我们的实现中,更容易发现错误的参数。价值对象还使我们能够获得关于某些价值的领域契约形状在一个中心位置,而不是在所有地方复制,我们需要验证的地方,例如一个论点看起来像一个电子邮件地址。

class mailer公共异步发送电子邮件(email,主题,text)//按形状验证,不按内容使用正则表达式if(!(email instanceof emailvalue))引发新类型错误(“email必须是emailvalue!”);/…}

更安全的,但更难理解

在我的项目中构建了这种技术的第四个迭代之后,我最近注意到一个缺点:它增加了调用代码的复杂性。让我们比较一下这两个例子。

没有值对象:

const mailer=require('@acme/mailer');const m=new mailer('localhost:25');m.sendmail('john@example.com',“你好”“文本……”;

使用值对象:

const {MaLever,smtphostvalue=require('@acme/mailer');//让我们共享全局域值对象!const emailvalue=require('@acme/values');const m=new mailer(new smtphostvalue('localhost',25);m.sendemail(新emailvalue('john@example.com'),“你好”“文本……”;

现在我们要求调用者首先使用正确的值对象构造参数,在调用实际方法之前。这完全符合快速失效但这里的问题是,我们将有关参数细节的知识转移到了外部世界。调用方需要确切知道要导入哪个值对象类,如果我们需要重构我们的实现,我们已经增加了可能会受到影响的代码表面。

我们还增加了代码执行所需的屏幕不动产,这给可读性和可理解性带来了损失。尤其是对于不熟悉这个概念的开发人员来说,像这样的构造似乎是反直觉的:

m.sendemail参数中的(/“new”?new emailvalue('john@example.com'),);

最后但并非最不重要:使用值对象的实例变得很麻烦。

成像发送电子邮件方法返回已发送的电子邮件:

class mailer公共异步发送电子邮件(email,主题,text)//按形状验证,不按内容使用正则表达式if(!(email instanceof emailvalue))引发新类型错误(`email必须是emailvalue!“$json.stringify(email)”给出.`);/…等待此消息。transport.send(…);返回电子邮件,主题,文本} }

对于呼叫者电子邮件现在是一个对象:

const letter=等待m.sendemail(new emailvalue('john@example.com'),“你好”'文本…')类型的letter.email//对象(emailvalue)

为了获得价值,他们需要知道一个emailValue有一个电子邮件财产:

letter.email.email/'john@example.com'

这很奇怪,导致原始的通过让它们实现toSTRIN()方法:

类emailvalue/…toString()返回this.email;}

因此,具有价值的消费者可以将其严格化:

`$letter.email `/'john@example.com'

除非Value对象不再是字符串,而是漂浮物或不能再有效地表示为字符串的复合值:

类smtphostvalue构造函数(hostname,端口)this.hostname=hostname this.port=端口

尽管人们可以直观地将其实现为这样:

`$新smtphostvalue('localhost',25)`/'本地主机:25'

这里的问题是不能使用字符串表示来构造值对象:

new emailvalue(`$new emailvalue('john@example.com')`)//此函数用于新的smtphostvalue(`$new smtphostvalue('localhost',25)`)//这不是

公平地说:在许多情况下,您只需要传递价值对象实例。但我的经验表明,在将数据编组到表示和从表示中编组时,它变得特别乏味,例如通过JSON发送或接收JSON输入时。

值类型:调用我,喜欢意味着它

我在寻找另一种方法,我想要实现的是信守价值对象的承诺,但是为了减少主叫方的开销。

一个设计决策是消除调用方了解值对象的需求,他们应该能够使用基元值调用方法……毕竟我们正在编写typescript代码,我不想在其中添加额外的bloat。此外,我还想解决装箱价值问题:您应该能够直接使用价值,而不是需要把手伸进箱子里.

class mailer公共异步发送电子邮件(电子邮件:string,主题:字符串,文本:字符串):承诺
        
         {…}}键入letter=email:string;主题:字符串;文本:字符串;}
        

我取消了价值观的束缚,但通过引入价值类型它建在输入输出TS它结合了运行时验证和类型脚本类型。

就是这样发送电子邮件实施:

class mailer公共异步发送电子邮件(电子邮件:string,主题:字符串,文本:字符串):承诺
        
         email=验证(emailvalue)(email);等待此消息。transport.send(…);返回电子邮件,主题,文本} }
        

让我们看看细节:

email=验证(emailvalue)(email);

验证是一个方法,它能够针对给定的值类型验证参数。电邮价值是这样的值类型。验证如果传递的参数无效,将引发类型错误。对于我们的方法来说,这意味着,我们保证电子邮件实际上包含一个字符串,根据我们的域规范,该字符串的电子邮件地址语法正确。我们还明确表示了开发人员的意图:电子邮件是有效的电子邮件地址.如果呼叫者传递的电子邮件地址无效,他们将收到类型错误。

重要的是要注意电子邮件类型逗留,在回信中,调用方现在可以自然地访问其属性:

const letter=等待m.sendemail('john@example.com',“你好”'text…')类型的letter.email/'john@example.com'

代码

这是验证

import*as t from'io ts';import pathreporter from'io ts/lib/pathreporter';/***此函数采用其中一种值类型来验证参数。**示例:*const u=validate(urlvalue)('https://example.com')*//type of u=='string'**@link https://github.com/gcanti/i o-ts*a:静态(运行时)类型*o:编码输出*i:解码输出*@throws类型错误*/export const validate=
        
         (T型)
         
          T.AlayyType
          
           ,A哦,i>,)=>(值:i):a=>const result=type.decode(值);if(result.isleft())引发新的类型错误(pathReporter.report(result.join(''));}返回result.value作为;;
          
         
        

电子邮件值类型的实现方式如下:

从“IO TS”导入*作为T;const emailregex=/.+\@.+\..+/;export const emailvalue=new t.类型
        
         (电子邮件)(S):S是字符串=>类型S=='字符串',(m)c)=>(m和emailRegex.测试(m)?t.成功(m):t.失败(m,c)a=>a);
        

总结

价值类型用于启用运行时验证参数。这提供了一种简单而富有表现力的方法,以确保它们的有效性,同时在代码中传递它们的语义含义,同时使其对调用者透明。