【问题标题】:String type to number字符串类型转数字
【发布时间】:2021-05-10 13:10:28
【问题描述】:

有没有办法将字符串类型文字(不是运行时值)转换为其对应的数字类型文字,例如'1'1?


我想将变量缩小为对象的键(注意程序员断言obj 的类型是准确的,如TS doesnt yet have support for it):

const isKeyOfExactTable = <O extends object, T extends string | number>(o: O, key: T): key is ??? => Object.hasOwnProperty.call(o, key);
// what to put here? -------------------------------------------------------------------------^^^

一直存在 JS 对象属性只能是字符串的问题,但每个成员访问都隐式转换数字。因此,TS 属性实际上可以是数字,即使不是,问题也会出现在成员访问时,只是提前一步。

从 TS 4.1 开始,模板文字类型解决了一个方向,这使得这几乎完全可能(如果任一侧有数字文字,则可以转换并检查),如果属性具有 number 索引,则可以取键中的所有数字。可悲的是,如果键是number,并且对象具有字符串作为属性,代表数字,那么似乎根本没有办法。甚至可以检查,确实有问题:

type HasStringNumber<T> = T extends `${number}`
  ? 'houston, we have a problem'
  : 'all systems green';
type Test<O extends object, K extends string | number> = number extends K
  ? HasStringNumber<keyof O>
  : 'all systems green';

但无法识别实际数字,例如这或多或少可以推断输入字符串:

type WhatNumber<T extends string> = T extends `${infer N}` ? N : 'Not a number';`

直接给出问题的输入/输出示例:

declare const o: { '1': 0 };
declare const k: number;
if (isKeyOfExactTable(o, k)) { /* typeof k is 1 */ }

【问题讨论】:

  • 这能回答你的问题吗? How to convert a string to number in TypeScript?
  • @Sisir 不,这不是该主题的欺骗 - 这是关于类型的。具有typescript 标签而不是javascript 的链接主题无论如何都是一个笑话,因为它实际上是一个javascript 问题(第一个答案甚至以“与javascript 完全一样”开头)。我将添加免责声明,以帮助忙碌的人更快地识别差异。

标签: typescript


【解决方案1】:

不,目前没有对 type StrToNum&lt;T extends string&gt; = ... 的直接支持,它与 type NumToStr&lt;N extends number&gt; = `${N}` 相反。看起来microsoft/TypeScript#42938 可能是跟踪此请求的问题。


目前,您所能做的就是解决它。如果您愿意将您的数字字符串集限制为那些表示小于某个合理较小值的非负整数的字符串,您可以将以下StrToNum 实现推到某个库中的某个地方:

type Nums = [
  0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
  10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
  20, 21, 22, 23, 24, 25, 26, 27, 28, 29
]; // make this as long as you need
type StrToNum<T extends string> =
  Extract<T extends keyof Nums ? Nums[T] : never, number>;

这通过索引到一个固定的tupleNums 来工作,其键被自动解释为数字字符串,其值是相应的数字。如果需要支持更大的数字,可以手动加长Nums。事实证明,就目前而言,这可能已经接近你能做到的最好水平了。

您可以尝试使用recursive conditional types 来计算这种StrToNum 的任意整数,方法是以编程方式构建元组。但是简单的实现会遇到小于 25 左右的数字的递归限制,这比上面的Nums 更糟糕。相反,您需要做一些更复杂的事情,比如让编译器计算相关数字的二进制数字,如@9​​87654324@。这将使您能够计算StrToNum&lt;"12345"&gt;。但即使是这种方法最终也会构建一个与所需输出长度相同的元组,因此StrToNum&lt;"8675309"&gt; 可能会崩溃或挂起编译器。这对我来说似乎不值得。


无论如何,StrToNum 的上述定义足以处理您的示例:

function isKeyOfExactTable<T extends object>(t: T, k: PropertyKey): k is
  keyof T | StrToNum<Extract<keyof T, string>> {
  return k in t;
}    

declare const o: { '1': 0 };
declare const k: number;
if (isKeyOfExactTable(o, k)) {
  k // 1
}

如果您需要任意大的数字,或者带有小数部分的数字,或者负数,或者需要指数符号的数字,我真的不知道有什么好的方法来实现它。

除了去 microsoft/TypeScript#42938 并给它一个 ?,我可能会尝试找出如何反转问题,以便我只需要处理将字符串文字转换为数字文字。例如:

function toStringKeyOfExactTable<T extends object, K extends number>(t: T, k: K):
  Extract<keyof T, `${K}`> | (`${K}` extends keyof T ? never : undefined) {
  return ((k in t) ? String(k) as `${K}` : undefined) as any;
}
const newKey = toStringKeyOfExactTable(o, k); // "1" | undefined
if (newKey !== undefined) {
  newKey // "1"
  const val = o[newKey] // 0
}
const newKey2 = toStringKeyOfExactTable(o, 1); // "1"
const newKey3 = toStringKeyOfExactTable(o, 2); // undefined

在这里,我们将数字键转换为字符串(或可能未定义),然后将该字符串用作键。这不是你想写的代码,但至少编译器可以遵循它。


Playground link to code

【讨论】:

  • 谢谢。注意:对此更通用的方法似乎也受到this issue 的限制。在极端情况下,变量将是declare const k: number | string,并且对象可能具有沿其他属性的索引。看来我要么完全放弃这个想法,要么至少大大限制输入。
猜你喜欢
  • 1970-01-01
  • 2014-12-08
  • 2019-10-09
  • 1970-01-01
  • 2012-02-15
  • 2018-08-16
  • 2017-02-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多