Để chuyển một chuỗi thành số trong TypeScript, hãy gọi Number(text). Kết quả có kiểu number, và là NaN khi chuỗi không phải số hợp lệ. Với chuỗi có ký tự thừa ở cuối, như "42px", hãy dùng parseInt(text, 10) hoặc parseFloat(text).
Cả bốn cách chuyển đổi đều trả về kiểu number. TypeScript không biết chuỗi có hợp lệ hay không; một chuỗi không hợp lệ vẫn cho ra một number, với giá trị là NaN.
Vì sao bạn phải chuyển đổi
Trường của form, tham số URL, biến môi trường và nội dung tệp đều đến dưới dạng chuỗi. TypeScript không cho bạn dùng chuỗi ở nơi cần một số:
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.
Phép tính với -, * và / trên một chuỗi là lỗi. + thì được phép nhưng sẽ nối chuỗi, nên "3" + 10 là chuỗi "310", đó là lý do phải chuyển đổi trước: Number(quantity) * 10.
Type assertion cũng không giúp được gì. quantity as number bị từ chối (TS2352), và ép qua as unknown as number chỉ làm trình biên dịch im lặng: lúc chạy, giá trị vẫn là chuỗi.
Number, parseInt, parseFloat và dấu + một ngôi
Bốn hàm cho kết quả giống nhau với đầu vào sạch và khác nhau ở mọi trường hợp còn lại. Bảng này là đầu ra thật của từng hàm khi chạy trên Node:
| Đầu vào | 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 |
Các quy tắc đằng sau bảng này:
Numbervà dấu+một ngôi hoàn toàn giống nhau. Chúng chuyển đổi cả chuỗi sau khi bỏ khoảng trắng hai đầu, hiểu được1e3và số hệ 16 dạng0x, và trả vềNaNnếu còn sót lại bất cứ ký tự nào. Chuỗi rỗng hoặc chỉ có khoảng trắng thành0.parseIntvàparseFloatđọc từ đầu chuỗi và dừng ở ký tự đầu tiên không dùng được, nên"42px"và"1,000"cho ra một phần của số. Chuỗi rỗng làNaN.parseIntbỏ phần thập phân và không hiểu số mũ:"1e3"dừng ở chữe.- Dấu phân cách số (
12_000) hợp lệ trong mã nguồn, nhưng không hợp lệ trong chuỗi.
Number là lựa chọn chặt chẽ hơn để kiểm tra đầu vào. parseInt và parseFloat hợp với chuỗi thật sự có kèm đơn vị hoặc hậu tố, như kích thước trong CSS.
Xử lý đầu vào không hợp lệ: NaN
NaN có kiểu number, nên trình biên dịch không bao giờ cảnh báo về nó. Bạn phải tự kiểm tra kết quả bằng Number.isNaN hoặc Number.isFinite:
Trả về number | undefined biến trường hợp thất bại thành một phần của kiểu, nên mọi nơi gọi hàm đều phải xử lý nó trước khi tính toán. Number.isFinite cũng loại Infinity, giá trị mà Number("Infinity") vẫn vui vẻ trả về.
Tránh dùng hàm toàn cục isNaN(x): nó chuyển đổi đối số trước, nên trong JavaScript isNaN("abc") là true. Khai báo của TypeScript chỉ chấp nhận number, nên truyền một chuỗi là lỗi TS2345, một gợi ý để dùng Number.isNaN trên giá trị đã chuyển đổi.
Chuỗi sang số nguyên
Luôn truyền cho parseInt đối số thứ hai, tức cơ số: 10 với chuỗi số thập phân. Đối số này không bắt buộc, nhưng nó thể hiện rõ ý định và quan trọng với các hệ cơ số khác. Để lấy số nguyên từ một chuỗi số thập phân, hãy chuyển đổi rồi làm tròn một cách tường minh.
parseInt cần một chuỗi: parseInt(12.7) là lỗi biên dịch (TS2345), và điều này có ích, vì trong JavaScript parseInt trên một số sẽ chuyển số đó thành chuỗi trước rồi cho kết quả sai với những giá trị như 0.0000005 (nó trả về 5). Hãy dùng Math.trunc cho số. Với số nguyên vượt quá Number.MAX_SAFE_INTEGER, BigInt(text) đọc được trọn giá trị (và ném ra SyntaxError với chuỗi không hợp lệ thay vì trả về NaN).
Số sang chuỗi
String(n), n.toString() hay `${n}` đều chuyển một số thành dạng chuỗi ngắn nhất của nó. Các phương thức định dạng cho bạn nhiều quyền kiểm soát hơn:
toFixed làm tròn giá trị nhị phân, không phải số thập phân bạn đã gõ, nên (1.005).toFixed(2) là "1.00": 1.005 được lưu thành một số nhỏ hơn 1.005 một chút. Với tiền tệ, hãy lưu số tiền dưới dạng số nguyên tính theo xu hoặc định dạng bằng Intl.NumberFormat.
String(n) cũng dùng được với null và undefined (cho ra "null" và "undefined"), trong khi n.toString() sẽ là lỗi biên dịch khi bật strict, vì giá trị có thể không tồn tại.
Tra cứu nhanh
| Việc cần làm | Code | Kiểu kết quả |
|---|---|---|
| Chuỗi sang số, chặt chẽ | Number(s) hoặc +s | number (có thể là NaN) |
| Số nguyên ở đầu chuỗi | parseInt(s, 10) | number (có thể là NaN) |
| Số thập phân ở đầu chuỗi | parseFloat(s) | number (có thể là NaN) |
| Kiểm tra kết quả | Number.isFinite(n), Number.isNaN(n) | boolean |
| Số nguyên lớn | BigInt(s) | bigint |
| Số sang chuỗi | String(n), n.toString(), `${n}` | string |
| Số chữ số thập phân cố định | n.toFixed(2) | string |
| Hệ cơ số khác | n.toString(16) | string |
| Dấu phân cách hàng nghìn | n.toLocaleString("en-US") | string |
Câu hỏi thường gặp
Làm sao chuyển chuỗi thành số trong TypeScript?
Dùng Number(text). Hàm này trả về một number: giá trị số với chuỗi số hợp lệ, NaN với mọi thứ khác, và 0 với chuỗi rỗng hoặc chỉ có khoảng trắng. Dùng parseInt(text, 10) hoặc parseFloat(text) khi chuỗi có thể có ký tự thừa ở cuối như "42px".
Có thể ép kiểu chuỗi thành số bằng as number không?
Không. "42" as number là lỗi biên dịch (TS2352), và kể cả khi ép qua unknown, một assertion chỉ đổi kiểu mà trình biên dịch nhìn thấy: lúc chạy, giá trị vẫn là chuỗi "42". Hãy chuyển đổi giá trị bằng Number(), parseInt() hoặc parseFloat().
Number() và parseInt() trong TypeScript khác nhau thế nào?
Number chuyển đổi cả chuỗi và trả về NaN nếu có phần nào không phải số, nên Number("42px") là NaN. parseInt đọc chữ số từ đầu chuỗi và dừng ở ký tự đầu tiên không phải chữ số, nên parseInt("42px", 10) là 42, và nó bỏ phần thập phân. Number("") là 0, còn parseInt("") là NaN.
Làm sao kiểm tra một chuỗi có phải số hợp lệ không?
Chuyển đổi rồi kiểm tra kết quả: const n = Number(text); if (text.trim() !== "" && Number.isFinite(n)) { ... }. Phần kiểm tra chuỗi rỗng quan trọng vì Number("") là 0. Dùng Number.isNaN, không dùng hàm toàn cục isNaN, vốn TypeScript cũng chỉ chấp nhận với đối số number.
Làm sao chuyển số thành chuỗi trong TypeScript?
String(n), n.toString() hoặc template literal `${n}` đều cho cùng kết quả. Dùng n.toFixed(2) để có số chữ số thập phân cố định, n.toString(16) cho hệ cơ số khác, và n.toLocaleString("en-US") để có dấu phân cách hàng nghìn.