避免在函数体内部欺骗参数的正确性

avoid cheecking arguments correctness inside the function body

提问人:john doe 提问时间:8/30/2021 最后编辑:john doe 更新时间:8/31/2021 访问量:119

问:

我最近注意到我们之前在代码库中编写的一个函数,我确信它在逻辑上是不正确的。函数是用 TypeScript 编写的,其主体如下所示:

const getUserInitials = (user: IUser | null) => {
   if (!user) { return ""; }
   
   return (user.FirstName.charAt(0) + user.LastName.charAt(0)).toUpperCase();
}

我很确定这是不正确的,因为这种函数签名允许传递不正确的参数,然后在内部尝试通过返回空字符串来修复不正确的参数段落。我认为这应该用用户参数只持有类型(而不是)来重写,并且这种参数检查应该在函数调用之外和之前进行,以便函数将始终接收到正确的参数。IUserIUser | null

在开发时,是否应该将其视为经验法则?我不是在谈论你收到一个整数的情况,你的函数只有在你的整数必须大于 100 时才需要运行。显然,您需要在函数内部检查这种事情,因为有人可能会传递您无法控制的错误值。这似乎是函数的合理关注点,我们显然需要在内部检查它以提供正确的函数输出。

但是在所呈现的案例中,情况完全不同 - 函数契约说它会给你一个用户的首字母,但在某些情况下,它只会返回一个空字符串“”(这不是用户首字母,所以在我看来,这是一个函数契约违规)。

我什至会更进一步说函数命名不正确,应该调用它

getInitials(user: IUser) => {...}

由于我们清楚地将 user 作为参数,因此函数名称中的“user”一词是多余的。

  1. 首先,我对违反契约的理解是否正确,以及在这种情况下在函数调用之前检查参数正确性的必要性?

  2. 如果我是对的 - 是否有任何情况,在静态类型语言中检查内部参数的正确性是一个很好的方法(跳过像我的例子中整数需要不超过 100 的情况,当然与语言类型系统无关)?

  3. 我关于函数名称冗余中“用户”的陈述是否正确?

先谢谢你。

与 TypeScript 语言无关 编码风格 重构代码 清理

评论

0赞 Guy Coder 8/30/2021
就我个人而言,我认为这更像是一组主观问题,但我不会投第一票。然而,我将支持主观的接近投票。
0赞 captain-yossarian from Ukraine 8/30/2021
我认为你应该和你的队友讨论一下
0赞 john doe 8/31/2021
@GuyCoder,如果问题讨论的是正确/不正确的设计,为什么会是主观的呢?或者你说的正确设计的整个主题是主观的?
0赞 Guy Coder 9/1/2021
我不是在回避你的评论,每次我写回复时,它都会变成一篇文章。您问题中的矛盾之处在于使用了我认为两个相互竞争的标签,并且.language-agnostictypescript
0赞 john doe 9/2/2021
@GuyCoder,一点也不矛盾。1. 与语言无关的标签 - 因为这个问题本质上是不可知的,几乎可以应用于所有主要的主流语言。2. TypeScript 标签 - 仅仅因为所讨论的具体示例是用 TypeScript 编写的,仅此而已。但问题不在于打字稿的细节,这是真的

答:

0赞 PBD 8/30/2021 #1

问题 1:是的,在使用该参数调用函数之前,您应该检查参数是否有效。

问题 3:根据经验,我会说不。GetInitials 含糊不清。在您的示例中,当您调用该函数时,您不一定知道它适用于用户的首字母缩写。

(这在面向对象编程中不是问题,因为在面向对象编程中,您将拥有 User.GetInitials(),因此在某种程度上 User 已经在名称中。

评论

0赞 john doe 8/31/2021
啊。不知道。使用这种逻辑,我们应该将 getInitialsByUserFirstnameAndUserlastname(string: firstName, string: lastName) 命名为(使用您的示例),我们也不一定看到它适用于 userLastname 和 userFirstname。不是吗?仅当您以某种方式忽略函数的参数或其类型时,GetInitials 才显得模糊不清。但这似乎不是问题,只要我们编写任何其他函数,名称中没有提到参数。但我明白你来自哪里。无论如何,感谢您的回答。
0赞 Matt Timmermans 8/30/2021 #2
  1. 你是部分正确的:

    答:检查函数的参数,并在特殊情况下设计其行为,这永远不会有什么坏处。在像 Typescript 这样的语言中尤其如此,它可以从 Javascript 调用,而 Javascript 是非类型化的。您可能是对的,没有人应该在没有有效用户的情况下调用此函数, 但这并不意味着没有人会这样做

    b:如果你能以有意义的方式解释一个 null 参数,那么这样做并返回一个合理的默认值通常是合理的。但是,我认为在这种情况下返回空字符串是合理的。在这种情况下,最好抛出错误。

    c:如果不能返回合理的默认值,则方法签名不应允许空参数...但是您仍然可以检查它们并且 抛出比默认情况下可能发生的任何错误更有意义的错误。不幸的是,许多人使用eslint的配置方式会抱怨这种检查,这通常是方法签名中这些添加的来源。| null

  2. 静态类型是不够的。如果您的语言阻止使用错误类型的参数有意义地调用函数,则无需检查类型。打字稿不是那种语言。

  3. 绝对不行。在 typescript 中,方法参数不是方法签名的一部分。 与 的功能相同。如果创建一个名为 的函数,则没有其他人可以在同一范围内创建另一个函数。不要以为无论你在做什么,都是一个值得一个不合格名字的特殊情况。getInitials(user: User)getInitials(firstName: string, lastName: string)getInitials

评论

0赞 john doe 8/31/2021
感谢您的扩展回复。3 - 您能详细说明一下吗?我一直认为函数签名是任何语言中定义的名称和参数的组合?
0赞 john doe 8/31/2021
“你可能是对的,没有人应该在没有有效用户的情况下调用这个函数,但这并不意味着没有人会这样做。这应该是参数不正确的调用者所关心的问题,而不是我所看到的我。如果我将设计我的所有函数,检查所有可能的错误用法和不正确的调用,这似乎是一个非常大的过度工程和某种过早的优化(当然不是在术语的直接理解中)。在一般情况下,我们有静态类型来拯救我们。将恭敬地不同意该声明(在一般情况下)。
0赞 Matt Timmermans 8/31/2021
回复 3.在许多语言中都是如此,但 Typescript 只是一个应用于 Javascript 的类型系统,而在 Javascript 中,方法仅按名称查找。因此,如果在对象上定义函数,则不能在同一对象或子类上同时定义具有不同参数的单独函数。xx
0赞 Matt Timmermans 8/31/2021
回复 1.祝贺!如果你接到一个电话,在半夜把你吵醒,因为你写的东西抛出了一个神秘的异常,那么你就可以把它归咎于给你传递无效论点的人。然而,我仍然会睡着。:)
0赞 john doe 8/31/2021
呵呵,我明白你从哪里来:)但是,无论如何,在一般情况下,关于所有可能检查的想法似乎有点太极端了。感谢您的谈话和回复!欣赏它!