在 TypeScript 中把字符串转换为数字,就调用 Number(text)。结果的类型是 number,当文本不是有效数字时,值为 NaN。如果文本末尾带有其他字符,比如 "42px",就用 parseInt(text, 10) 或 parseFloat(text)。
这四种转换返回的类型都是 number。TypeScript 并不知道文本是否有效;无效的字符串得到的仍然是 number,只是值为 NaN。
为什么必须转换
表单字段、URL 参数、环境变量和文件内容拿到时都是字符串。TypeScript 不允许你把字符串用在需要数字的地方:
index.ts(3,23): error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type.
对字符串使用 -、* 和 / 做算术运算会报错。+ 是允许的,但它会拼接字符串,所以 "3" + 10 得到的是字符串 "310",这就是为什么必须先转换:Number(quantity) * 10。
类型断言也帮不上忙。quantity as number 会被拒绝(TS2352),而用 as unknown as number 强行断言只是让编译器闭嘴:运行时这个值仍然是字符串。
Number、parseInt、parseFloat 与一元加号对比
这四种方法在干净的输入上结果一致,其他情况下各不相同。下表是它们在 Node 上的实际输出:
| 输入 | Number(s) | +s | parseInt(s, 10) | parseFloat(s) |
|---|---|---|---|---|
"42" | 42 | 42 | 42 | 42 |
"3.99" | 3.99 | 3.99 | 3 | 3.99 |
"42px" | NaN | NaN | 42 | 42 |
"" | 0 | 0 | NaN | NaN |
" 3 " | 3 | 3 | 3 | 3 |
"1e3" | 1000 | 1000 | 1 | 1000 |
"0x1F" | 31 | 31 | 0 | 0 |
"1,000" | NaN | NaN | 1 | 1 |
"12_000" | NaN | NaN | 12 | 12 |
"abc" | NaN | NaN | NaN | NaN |
表格背后的规则:
Number和一元+完全相同。它们会先去掉首尾空白再转换整个字符串,能识别1e3和0x十六进制,只要有剩余字符就返回NaN。空字符串或只含空白的字符串会变成0。parseInt和parseFloat从开头读起,遇到第一个用不了的字符就停下,所以"42px"和"1,000"会得到部分数字。空字符串得到NaN。parseInt会丢掉小数部分,也不认识指数:"1e3"读到e就停了。- 数字分隔符(
12_000)在源代码中有效,在字符串中无效。
验证输入时,Number 是更严格的选择。parseInt 和 parseFloat 适合确实带有单位或后缀的文本,比如 CSS 尺寸。
处理无效输入:NaN
NaN 的类型是 number,所以编译器从不会对它发出警告。你需要自己用 Number.isNaN 或 Number.isFinite 检查结果:
返回 number | undefined 让失败成为类型的一部分,每个调用方都必须先处理它,才能做算术运算。Number.isFinite 还会拒绝 Infinity,而 Number("Infinity") 会照样返回它。
避免使用全局的 isNaN(x):它会先转换参数,所以在 JavaScript 中 isNaN("abc") 是 true。TypeScript 的声明只接受 number,传入字符串会报错 TS2345,这正是在提示你:对已经转换好的值使用 Number.isNaN。
字符串转整数
调用 parseInt 时一定要传第二个参数,也就是基数:十进制文本传 10。它是可选的,但能写明意图,对其他进制也很重要。要从带小数的字符串得到整数,先转换,再显式取整。
parseInt 期望的参数是字符串:parseInt(12.7) 会报编译错误(TS2345),这一点很有用,因为在 JavaScript 中对数字调用 parseInt 会先把它转成文本,对 0.0000005 这样的值会给出错误的答案(返回 5)。数字请用 Math.trunc。对于超过 Number.MAX_SAFE_INTEGER 的整数,BigInt(text) 能解析出完整的值(遇到无效文本时会抛出 SyntaxError,而不是返回 NaN)。
数字转字符串
String(n)、n.toString() 和 `${n}` 都能把数字转换为最短的字符串形式。格式化方法能提供更多控制:
toFixed 对二进制值取整,而不是对你写下的十进制数,所以 (1.005).toFixed(2) 是 "1.00":1.005 实际存储的值略小于 1.005。处理金额时,用整数“分”来保存,或者用 Intl.NumberFormat 来格式化。
String(n) 对 null 和 undefined 也有效(分别得到 "null" 和 "undefined"),而在 strict 下 n.toString() 会报编译错误,因为这个值可能不存在。
速查表
| 任务 | 代码 | 结果类型 |
|---|---|---|
| 字符串转数字(严格) | Number(s) 或 +s | number(可能是 NaN) |
| 从文本开头取整数 | parseInt(s, 10) | number(可能是 NaN) |
| 从文本开头取小数 | parseFloat(s) | number(可能是 NaN) |
| 检查结果 | Number.isFinite(n)、Number.isNaN(n) | boolean |
| 大整数 | BigInt(s) | bigint |
| 数字转字符串 | String(n)、n.toString()、`${n}` | string |
| 固定小数位 | n.toFixed(2) | string |
| 其他进制 | n.toString(16) | string |
| 千位分隔符 | n.toLocaleString("en-US") | string |
常见问题
TypeScript 中如何把字符串转换为数字?
用 Number(text)。它返回一个 number:有效的数字文本得到对应的值,其他情况得到 NaN,空字符串或只含空白的字符串得到 0。当文本末尾可能带有其他字符(比如 "42px")时,用 parseInt(text, 10) 或 parseFloat(text)。
能用 as number 把字符串转成数字吗?
不能。"42" as number 会报编译错误(TS2352),即使通过 unknown 强行断言,断言也只改变编译器看到的类型:运行时这个值仍然是字符串 "42"。要用 Number()、parseInt() 或 parseFloat() 真正转换值。
TypeScript 中 Number() 和 parseInt() 有什么区别?
Number 转换整个字符串,只要有一部分不是数字就返回 NaN,所以 Number("42px") 是 NaN。parseInt 从开头读取数字,遇到第一个非数字字符就停止,所以 parseInt("42px", 10) 是 42,而且会丢掉小数部分。Number("") 是 0,而 parseInt("") 是 NaN。
如何检查一个字符串是不是有效的数字?
先转换,再检查结果:const n = Number(text); if (text.trim() !== "" && Number.isFinite(n)) { ... }。空字符串检查很重要,因为 Number("") 是 0。用 Number.isNaN,不要用全局的 isNaN,而且 TypeScript 本来也只接受给后者传 number 参数。
TypeScript 中如何把数字转换为字符串?
String(n)、n.toString() 或模板字符串 `${n}` 得到的结果都一样。需要固定小数位时用 n.toFixed(2),需要其他进制时用 n.toString(16),需要千位分隔符时用 n.toLocaleString("en-US")。