🔎 搜索词
ts2365
关系比较
二元运算符 <
二元运算符 >
二元运算符 <=
二元运算符 >=
TypeScript 5
🕗 版本与回归信息
- v4.9.5 对这段代码没有错误。
- TS 5.0 为
lt1
和lt2
添加了错误,我个人认为这些错误是好的 — 它们提倡更明确的代码 — 但实际上它们是安全的类型组合(Improve comparison operators type checking to disallow unions containing numbers as an operand #52048, ann)。 - 然而,
lt3
、lt4
和lt5
在运行时是危险的,但被TS允许。
⏯️ Playground链接
链接 — 运行并查看日志
💻 代码
const show = (a: any, ltResult: boolean, b: any) =>
`${JSON.stringify(a)}${ltResult ? ' < ' : ' >='}${JSON.stringify(b)}\t`
// These two show TS2365 error on '<' operators (TS 5.2.2),
// but the allowed arg combinations actually always compared numerically:
const lt1 = (a: string | number, b: number ) => show(a, a < b, b)
const lt2 = (a: number , b: string | number) => show(a, a < b, b)
// These three TS 5.2.2 does NOT complain, but are unsafe!
// They allow string<string which compares lexicographically AND
// one string one number which which compare numerically
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Less_than#description
const lt3 = (a: string | number, b: string ) => show(a, a < b, b)
const lt4 = (a: string , b: string | number) => show(a, a < b, b)
const lt5 = (a: string | number, b: number | string) => show(a, a < b, b)
// Only including calls allowed by argument types:
console.log(`lt1: ${lt1(12, 9)} ${lt1('12', 9)}`)
console.log(`lt2: ${lt2(12, 9)} ${lt2(12, '9')}`)
// lt3-5 are 100% TS-clean but mix true/false results depending on run-time types!
console.log(`lt3: ${lt3(12, '9')} ${lt3('12', '9')}`)
console.log(`lt4: ${lt4('12', 9)} ${lt4('12', '9')}`)
console.log(`lt5: ${lt5(12, 9)} ${lt5('12', 9)} ${lt5(12, '9')} ${lt5('12', '9')}`)
🙁 实际行为
lt1–2 给出错误 Operator '<' cannot be applied to types 'string | number' and 'number'.
,反之亦然。
字符串与数字的比较实际上会将双方都转换为数字,因此这些比较始终是数值性的 — 运行Playground并查看日志,您会发现这里始终说 12 >= 9。
lt3-5 的例子反而没有TS错误,但允许一个字符串 < 字符串的情况!
只有在 *双方都是字符串(或将它们转换为原始字符串作为字符串,尽管有数字提示)的情况下,JS才会进行字典序字符串比较。
您会在Playground日志中看到这些返回混合的 < 和 >= 结果,具体取决于它们在运行时是否都是字符串或至少有一个数字。💥
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Less_than#description, https://262.ecma-international.org/#sec-islessthan
🙂 预期行为
lt3: Operator '<' cannot be applied to types 'string | number' and 'string'.
lt4: Operator '<' cannot be applied to types 'string' and 'string | number'.
lt5: Operator '<' cannot be applied to types 'string | number' and 'string | number'.
关于此问题的其他信息
我认为 lt5 情况是最“有趣”的,因为两边的类型相同,从类型系统的Angular 来看,它们都是 <
可以处理的有效子集。
特别是,这就是您编写代码以允许 string
< string
且 number
< number
在运行时执行的方式,这两种组合都是完全合法的。
但字典序与数值比较是语义上不同的操作(恰好共享相同的运算符),实际上您在编写代码时通常只希望其中一个含义。(参见 #49661,其中询问的是 val + val
。那里的答案是它与类型有关而不是身份,但我认为更根本的答案是您的意思是要么加法要么连接。)
我不知道关于更广泛的类型(如 any
< any
)应该怎么说 — 同样的论点适用于您只想表达其中一个含义的情况,但强制执行这一点将破坏TS逐步类型化的初衷。cc @Andarist@RyanCavanaugh
4条答案
按热度按时间iswrvxsc1#
我们有一些表单字段,用户在其中输入数字,有时我们会将字符串/数字表示混合使用(JS代码库逐渐转换为TS...)
因此,现在我们有很多验证需要执行
string | number
。大多数验证是e.g.value >= 1
,而TS 5开始警告这种类型,但实际上是安全的——另一方面,我们有一个min_field <= max_field
验证,可能非常危险,并可以从错误中受益!(嗯,其实min_field和max_field现在被指定为
any
,因为验证器需要allValues: object
,而使用redux-form和Formik进行每个字段的类型标注更加繁琐;我们还没有尝试过这个“高悬果实”;在处理完类型为allValues
之前,我们可能会收紧我们的NodesInput组件以存储单个已知类型;但是想象一下一个较小的应用程序,验证接近具体字段的情况。)thtygnil2#
我认为lt5案例是最"有趣"的,因为两边的类型相同,从类型系统的Angular 来看,它们是可处理的有效子集。
左侧参数可以是
string
,右侧参数可以是number
。nafvub8i3#
是的,我关于lt5被认为是“有趣”的文章实际上是我在扮演反面角色。你是对的,“两边的类型相同”是一种有意义的手势。
我的观点是,值得阻止的风险行为不仅仅是混合
string
<number
——虽然这些类型定义得很好,但风格很差,因为人们必须查找它们将如何执行——而是可以解析为运行时进行字典或数字比较的类型。一个通用的排序函数,可以接受
any[]
或string[] | number[]
等参数,可以合理地定义为使用“自然JS < 排序顺序”:一个要按数字排序的数字数组,一个按字典序排序的字符串数组,运行时选择将是已知的。这样的一个函数将在某个地方进行类似lt5的比较!
在我看来,这种情况足够罕见,以至于需要用户停下来思考并添加
ts-ignore
吗?lp0sw83n4#
在之前的讨论中,尝试修复了#52807。