【问题标题】:avoid cheecking arguments correctness inside the function body避免在函数体内检查参数的正确性
【发布时间】:2021-08-30 13:43:11
【问题描述】:

我最近注意到我们代码库中较早编写的一个函数,我确信它在逻辑上是不正确的。函数是用 TypeScript 编写的,它的主体是这样的:

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

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

在开发时是否应将其视为经验法则?我不是在谈论当你收到一个整数并且你的函数只有在你的整数必须不超过 100 时才需要运行的情况。显然,你需要检查函数内部的那种东西,因为有人可以通过不正确的值,这是您无法控制的。这似乎是对函数的合理关注,我们显然需要在内部检查它以提供正确的函数输出。

但在本例中,情况完全不同 - 函数合同说它会给你一个用户的首字母,但在某些情况下,它只会返回一个空字符串“”(这不是用户首字母,所以它是一个如我所见,违反了功能合同)。

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

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

因为我们清楚地将用户作为参数,所以函数名中的“用户”一词是多余的。

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

  2. 如果我是正确的 - 是否存在在静态类型语言中检查内部参数正确性的好方法(在我的示例中跳过案例,整数需要不超过 100 和类似的案例,哪个当然与语言类型系统无关)?

  3. 我在函数名冗余中关于“用户”的说法正确吗?

提前谢谢你。

【问题讨论】:

  • 就我个人而言,我发现这更像是一组主观问题,但我不会投第一次近距离投票。但是,我会支持主观投票。
  • 我觉得你应该和你的队友讨论一下
  • @GuyCoder 如果问题讨论了正确/不正确的设计,这怎么会是主观的?还是说正确设计的整个主题都是主观的?
  • 我并没有回避您的评论,每次我写回复都会变成一篇文章。您问题中的矛盾之处在于使用了我认为的两个相互竞争的标签,language-agnostictypescript
  • @GuyCoder 好吧,完全没有矛盾。 1. 与语言无关的标签——因为这个问题本质上是不可知的,几乎可以应用于所有主要的主流语言。 2. typescript 标签 - 仅仅因为所讨论的具体示例是用 typescript 组成的,仅此而已。但问题不在于打字稿细节,这是真的

标签: typescript language-agnostic coding-style refactoring code-cleanup


【解决方案1】:

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

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

(在面向对象的编程中,这不是问题,在这种编程中你会有 User.GetInitials() 所以在某种程度上 User 已经在名字中了。)

【讨论】:

  • 啊。不知道那件事。使用这种逻辑,我们应该将 getInitialsByUserFirstnameAndUserlastname(string: firstName, string: lastName) 命名为(使用您的示例)我们也不一定看到它用于 userLastname 和 userFirstname。不是吗?只有当您以某种方式忽略函数的参数或其类型时,GetInitials 才会显得模糊。但是,当我们编写任何其他没有在名称中提及参数的函数时,这似乎并不是一个问题。但我知道你来自哪里。无论如何感谢您的回答。
【解决方案2】:
  1. 你部分正确:

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

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

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

  2. 静态输入是不够的。如果您的语言阻止使用错误类型的参数有意义地调用您的函数,那么您永远不需要检查类型。 Typescript 不是那种语言。

  3. 绝对不是。在打字稿中,方法参数不是方法签名的一部分。 getInitials(user: User)getInitials(firstName: string, lastName: string) 功能相同。如果你创建了一个名为getInitials 的函数,那么没有其他人可以在同一范围内创建另一个函数。不要认为你所做的一切都是一个特殊的情况,应该得到一个不合格的名字。

【讨论】:

  • 感谢您的回复。 3 - 你能详细说明一下吗?我一直认为函数签名是任何语言定义的名称和参数的组合?
  • “你可能是对的,没有有效用户的情况下没有人应该调用这个函数,但这并不意味着没有人会。”这应该是参数不正确的调用者所关心的,而不是我所看到的我。如果我将设计我所有的函数来检查每一个可能的错误使用和错误调用,这似乎是一个非常大的过度工程和某种过早的优化(当然不是对一个术语的直接理解)。在一般情况下,我们有静态类型来拯救我们。将恭敬地不同意该声明(在一般情况下)。
  • Re 3. 在许多语言中确实如此,但 Typescript 只是一种应用于 Javascript 的类型系统,而在 Javascript 中,方法仅通过名称查找。所以如果你在一个对象上定义了一个函数x,那么你不能同时在同一个对象或子类上定义一个带有不同参数的单独函数x
  • Re 1. 恭喜!如果你在半夜接到一个叫醒你的电话,因为你写的东西抛出了一个神秘的异常,那么你就可以把它归咎于给你传递无效参数的人。然而,我还是会睡着的。 :)
  • 哈,我知道你来自哪里 :) 但无论如何,关于一般情况下所有可能检查的想法似乎有点过于极端。感谢您的谈话和回复!欣赏它!
猜你喜欢
  • 1970-01-01
  • 2020-10-19
  • 1970-01-01
  • 2011-05-19
  • 2022-05-18
  • 1970-01-01
  • 2020-01-26
  • 1970-01-01
相关资源
最近更新 更多