TypeScript 禁止将类型为'symbol'的值传递给'Number'函数/构造函数或'String'构造函数,

wz1wpwve  于 5个月前  发布在  TypeScript
关注(0)|答案(9)|浏览(57)

建议

NumberConstructorStringConstructor 接口更改为不允许传递类型为 Symbol 的值。目前它接受类型为 any 的值。
尝试调用 Number(Symbol())new Number(Symbol())new String(Symbol()) 时,所有操作都会在运行时抛出 TypeError 异常。

注意:String(Symbol()) 没有问题,只有 new String(Symbol()) 会抛出异常。

用例

当将类型为 any 的值传递给 Number 时,存在该值为 symbol 并抛出 TypeError 的风险。
对象键被明确允许为类型 symbol(如 number | string | symbol)。将任意对象键传递给 Number 可能也会抛出 TypeError 异常。

示例

检查一个值是否可以强制转换为数字

我刚刚遇到这个问题,当我检查一个对象键是否可以是有效的数组索引时。以下应该是一个错误,因为如果 value 是类型 symbol,它将抛出异常。

function keyCanBeArrayIndex(value: string | number | symbol): boolean {
  return !Number.isNaN(Number(value));
}

让这成为无效将会迫使我编写类似下面的代码:

function canBeArrayIndex(value: string | number | symbol): boolean {
  try {
    return !Number.isNaN(Number(value));
  } catch {
    return false;
  }
}

检查清单

我的建议满足以下准则:

  • 这不会对现有的 TypeScript/JavaScript 代码造成破坏性改变
  • 这不会改变现有 JavaScript 代码的运行时行为
  • 这可以在不根据表达式的类型发出不同的 JS 的情况下实现
  • 这不是一个运行时特性(例如库功能、带有 JavaScript 输出的非 ECMAScript 语法等)
  • 这个特性将与 TypeScript's Design Goals 的其他部分保持一致。
fwzugrvs

fwzugrvs1#

这样的代码可以工作,但是它有一个非常无助的错误文本:

type NotSymbol<T> = T extends symbol ? never : T;
interface NumberConstructor {
    new<T>(value?: NotSymbol<T>): Number;
    <T>(value?: NotSymbol<T>): number;
    //...
}
au9on6nz

au9on6nz2#

你是否经常将 symbol 传递给 Number 函数?根据我的经验,这类更改在引起破坏的程度上并不会带来很大的回报。

ccrfmcuu

ccrfmcuu3#

@DanielRosenwasser 不多,没有。然而,这种情况确实可能发生。就我个人而言,我在创建一个 Proxy 来 Package 数组时遇到了上述示例。我想捕获 set 操作,但只有在键是数字的情况下才这样做。对于今天大多数键来说,函数运行得很好,但在未来几年随着符号的普及,程序可能会开始崩溃。

是的,这不是你每天都会遇到的问题,但这就是使用 TypeScript 的好处——它应该在你意识到之前防止你犯这种错误。

此外,当传递一个 symbol 时抛出错误的行为是非常不直观的。当我们执行 ${Symbol()} 时,TS 报错:

Implicit conversion of a 'symbol' to a 'string' will fail at runtime. Consider wrapping this expression in 'String(...)'.

首先,这意味着 TS 已经在尝试防止这种情况的发生,其次,它明确表示 String(Symbol()) 是完全有效的,这是事实。但是基于这一点,我认为像大多数人一样,我也假设 Number(Symbol()) 也是有效的,特别是考虑到 任何其他值 时, Number 将产生一个数字或 NaN

pbpqsu0x

pbpqsu0x4#

我认为这里部分问题是,据我所知,TypeScript没有一种方式来表达“除了symbol之外的任何类型”的约束。否定类型可以允许这种约束,但我们现在还没有(参见#29317)。根据我的经验,人们确实会使用Number()构造函数来进行各种运行时转换,因此过度限制其参数到例如number | string将很可能破坏实际代码。

ig9co6j1

ig9co6j15#

在我的经验中,人们确实会使用 Number() 构造函数进行各种运行时转换。这正是我们应该为这种情况报错的原因——人们正在使用 Number() 进行运行时转换,我确信大多数人并不知道传递一个符号会导致错误,从而导致程序崩溃。
我完全同意它不应该过于受限。除非有其他东西在传递给 Number 时会抛出错误(如 {toString: null, valueOf: null} 在传递给 String 时所做的那样),否则所有其他情况都应该是有效的。

esbemjvw

esbemjvw6#

考虑到 #22251 被合并以防止在模板字符串中使用符号,这显然是有可能的,而且我认为应该以同样的方式处理。所以也许我们允许 String(any) 但不允许 String(symbol) ,就像那个修复一样。这种行为将处理我的例子情况,这可能是(如果我没有弄错的话)你可能会遇到的最常见的情况。

在这种情况下,也许可以设置一个选项来禁止在模板字符串和 Number 情况下使用 any (也许对于模板字符串已经有了,我不熟悉它)。

gv8xihay

gv8xihay7#

经过一些探索,我发现尝试调用 new String(Symbol())(当然这并不是一个好主意,但仍然有可能)也会抛出 TypeError。我已经更新了原始问题以反映这一点,因为它们几乎是完全相同的情况。
总之:

String(Symbol()); // fine
new String(Symbol()); // TypeError

Number(Symbol()); // TypeError
new Number(Symbol()); // TypeError

Boolean(Symbol()); // fine
new Boolean(Symbol()); // fine

Object(Symbol()); // fine
new Object(Symbol()); // fine

TypeScript 接受所有这些而不抱怨。

ozxc1zmp

ozxc1zmp8#

根据标准,任何需要将符号强制转换为字符串的情况(除了特殊情况String(symbol))都会抛出错误。这里可能有很多内容需要覆盖。

9avjhtql

9avjhtql9#

是的,任何时候一个符号被隐式强制转换为字符串都会抛出错误,这正是应该发生的情况。这种行为已经非常有用,并且按预期工作。
问题在于,现在TS对于一些符号类型转换的情况会抛出错误,但对于其他情况则不会。我们只需要处理这些其他情况即可。

相关问题