【问题标题】:Why TypeScript lets me add a string and a number? Can I prevent it?为什么 TypeScript 允许我添加字符串和数字?我可以阻止它吗?
【发布时间】:2019-02-05 16:34:51
【问题描述】:

以下代码

let k = "1";
k += 1;
console.log(k);

即使在严格模式下也可以使用 TypeScript 正确编译。我本来预计 tsc 会因Cannot add a string and an integer. 之类的错误而失败。为什么构建成功?我可以防止这种危险行为吗?

【问题讨论】:

  • 字符串和数字之间的+ 运算符在 TypeScript 和 JavaScript 中有效。为什么要阻止这种情况?
  • 因为我希望 TypeScript 强制显式强制。我编写的代码 99% 的时间都是编程错误,我希望 TS 在构建时尽可能长地引发它们。我想不出任何一种强类型语言是允许且有意义的。
  • 对于有价值的东西,相反是非法的:如果 knumber,TypeScript 将在 k += "1" 上出错。

标签: typescript


【解决方案1】:

简而言之:因为正如@Explosion Pills 已经指出的那样,它是有效的 JS 和 Typescript。

原因是k += 1 只是k = k + 1 的语法糖,人们期望它是11。 所以这样的事情也有效:

let text = "hello ";
text += "world!"
console.log(text); 

最后是 hello world!

【讨论】:

  • 好吧,在您的示例中,您将字符串与字符串连接起来。这个问题绝对不是关于+= 运算符。
  • @user8808265 好吧 typescript 只是 javascript 的超集,您使用的语法在 javascript 中也是有效的。
  • @Niladri let i = 1; i = 1 + ""; 在 JS 中有效,在 TS 中无效,因为它改变了“i”类型。所以这可能不是真正的原因。
【解决方案2】:

“它是有效的,因为它在 JS 中有效”在为什么某个操作不是类型错误的上下文中是一个非答案;见What does "all legal JavaScript is legal TypeScript" mean?

在 JavaScript 中,像 alert("Your position in the queue is " + queuePos) 这样的代码是惯用且常见的——它通常写成 "str" + num.toString()

TypeScript 的立场是,惯用的 JS 不应该导致类型错误(如果可行)。这意味着 string + number 是允许的强制转换。

+= 应该做什么的问题是在两个选项之间进行选择:

  • 一致性x = x + y 应该与 x += y 相同
  • 安全x += y通常在 stringnumber 操作数之间完成,因此应该是非法强制

这两种选择都是明智且有根据的; TypeScript 恰好选择了第一个。

【讨论】:

  • 非常感谢。这确实比“所有 JS 都是有效的 TS”的事情更有说服力。我发现"str" + (5).toString() 比惯用形式更明确(我觉得这很危险),但这很可能来自我自己的背景和敏感性。
  • 哦,我刚刚阅读了您链接的 SO 问题,它确实将所有这些和 TS 选择纳入了视野。 ?
【解决方案3】:

恐怕目前没有办法阻止这种转换,除了构建您自己的抽象。像这样的东西(诚然,不是很优雅):

function safe_add(x: string, y: string): string { return x + y; }

let x = "1";
x = safe_add(z, 1); // Argument of type '1' is not assignable to parameter of type 'string'.

根据"All legal JavaScript is legal TypeScript",TypeScript 类型检查的通常策略是防止出现明显错误且从未真正有用的情况。例如,阻止将字符串传递给Math.max。然而,与此示例不同的是,字符串和数字之间的 + 运算符尽管并不总是可取的,但它确实是一个有效的操作,并且在实践中经常使用而被编译器禁止。由于x += y 等价于x = x + y,并且当x 是一个字符串时总是产生一个字符串,所以赋值本身也是有效的。这是编译器最有可能保持正常的情况之一。 Issue #20131 旨在让更多操作发出警告,但特别不包括这个。

正如您可能已经理解的那样,相反的情况被成功地阻止了,因为变量不应该使用 add-assignment 运算符更改其类型。

let y = 1;
y += "1"; // Type 'string' is not assignable to type 'number'

另见:

【讨论】:

  • +1。我认为变量类型不变的事实确实是支持不禁止这一点的主要论据。尽管我倾向于认为它有点过于宽松,但它并不像看起来那么糟糕,其余的都是品味问题,与 SO 无关 :-) 。非常感谢您的宝贵时间。
  • @user8808265 实际上,这主要取决于语言的设计方式。作为静态类型的欣赏者,我也更喜欢我的代码不要做出令人惊讶的假设。例如,Rust(我目前在这里比较活跃的语言)几乎没有隐式强制。
【解决方案4】:

您可以使用 TypeScript-ESLint 规则 restrict-plus-operands 来防止这种意外:

两个变量相加时,操作数必须是数字类型或字符串类型。

配置示例:

"restrict-plus-operands": true

如果启用该规则,则以下代码:

const x = 5;
const y = 'foo';
const z = x + y;
console.log(z);

将导致:

错误:.ts - '+' 操作的操作数必须是两个字符串或两个数字,但找到 5 + "foo"。考虑使用模板文字。

该规则不仅禁止在字符串和数字之间使用+,还禁止在左侧类型与右侧类型不同时使用+=

【讨论】:

    【解决方案5】:

    TSLint 建议使用模板文字而不是强制字符串和数字变量, 例如:'Hello ' + person.name 将变为 `Hello ${person.name}`

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-03
      • 1970-01-01
      • 2015-09-28
      • 2017-09-13
      • 1970-01-01
      • 2014-10-07
      相关资源
      最近更新 更多