Question
How can I correctly check whether a string contains a numeric value in TypeScript?
Calling isNaN directly with a string causes a TypeScript error because its typed argument is a number:
isNaN("9BX46B6A");
Using parseFloat does not solve the validation problem because it accepts a valid numeric prefix. For example, parseFloat("9BX46B6A") returns 9, so this produces an incorrect result:
isNaN(parseFloat("9BX46B6A")); // false
How should this be implemented correctly, instead of comparing the parsed number's string length with the original input?
static isNaNModified = (inputStr: string) => {
const numericRepr = parseFloat(inputStr);
return isNaN(numericRepr) || numericRepr.toString().length !== inputStr.length;
};
Short Answer
You will learn why parseFloat() is not a strict string validator, how Number() and Number.isFinite() can validate numeric input, and when a regular expression is the better choice for decimal-only formats.
Concept
A numeric-string check has two separate jobs:
- Convert or inspect the whole input.
- Reject formats your application does not allow.
parseFloat() is designed for parsing text that starts with a number. It intentionally stops when it reaches an invalid character:
parseFloat("9BX46B6A"); // 9
parseFloat("12px"); // 12
That behavior is useful when reading values such as CSS-like text, but it is not appropriate when validating user input.
For general JavaScript numeric conversion, use Number() instead. It requires the entire non-whitespace string to be convertible:
Number("9BX46B6A"); // NaN
Number("12px"); // NaN
Number("12.5"); // 12.5
Then use Number.isFinite() to reject NaN, Infinity, and -Infinity:
function isNumericString(value: string): boolean {
const trimmed = value.trim();
return trimmed !== "" && Number.isFinite(Number(trimmed));
}
The empty-string check matters because Number("") and Number(" ") both return 0. Usually, an empty form field should not count as a number.
The word numeric can mean different things. Number() accepts JavaScript number formats such as "0xFF" (hexadecimal). If you only allow ordinary decimal input such as "12", "-3.5", or "1e4", validate the textual format with a regular expression instead.
Mental Model
Think of parseFloat() as a cashier who reads a price label only until it becomes confusing. Given "9BX46B6A", the cashier reads 9 and ignores the remaining letters.
Number() is like a strict scanner: it must understand the entire label or it rejects it.
A regular expression is like a custom admission rule at a door. You can say exactly which characters and layouts are allowed, such as optional + or -, digits, one decimal point, and an optional exponent.
Syntax and Examples
For a general finite JavaScript number, trim whitespace, reject an empty value, convert with Number(), and test with Number.isFinite():
function isNumericString(value: string): boolean {
const trimmed = value.trim();
return trimmed !== "" && Number.isFinite(Number(trimmed));
}
console.log(isNumericString("42")); // true
console.log(isNumericString(" -3.14 ")); // true
console.log(isNumericString("1e6")); // true
console.log(isNumericString("9BX46B6A")); // false
console.log(isNumericString("12px")); // false
console.log(isNumericString("")); // false
console.log(isNumericString("Infinity")); // false
If your input must be a decimal number only, use a pattern. This version permits integers, decimals, signs, and scientific notation, but rejects hexadecimal values:
const decimalNumberPattern = /^[+-]?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?$/;
function isDecimalString(value: string): boolean {
return decimalNumberPattern.test(value.trim());
}
console.log(isDecimalString("42")); // true
console.log(isDecimalString(".5")); // true
console.log(isDecimalString("-1.25e3")); // true
console.log(isDecimalString("0xFF")); // false
console.log(isDecimalString("12px")); // false
Use the first function when your application accepts JavaScript-style numeric values. Use the second when the text format itself matters.
Step by Step Execution
Consider this function and input:
function isNumericString(value: string): boolean {
const trimmed = value.trim();
return trimmed !== "" && Number.isFinite(Number(trimmed));
}
const result = isNumericString(" 9BX46B6A ");
Step by step:
value.trim()removes the surrounding spaces, producing"9BX46B6A".trimmed !== ""istrue, so evaluation continues.Number("9BX46B6A")attempts to convert the whole string.- The letters make the complete conversion invalid, so the result is
NaN. Number.isFinite(NaN)isfalse.- The function returns
false.
Now try isNumericString(" 9 "):
- Trimming produces
"9". - The string is not empty.
Number("9")returns9.Number.isFinite(9)istrue.- The function returns
true.
Real World Use Cases
Numeric-string validation is common when values arrive as text:
- Form inputs: Validate quantities, prices, ages, and measurements before submitting a form.
- Query parameters: Check a URL value such as
?page=3before using it for pagination. - CSV imports: Reject rows where an amount contains accidental text like
"19USD". - Configuration files: Validate environment variables such as
PORT="3000". - API boundaries: Verify string fields from external systems before calculations or database writes.
Validation should happen before using the converted number:
const pageText = "3";
if (!isNumericString(pageText)) {
throw new Error("page must be a finite number");
}
const page = Number(pageText);
Real Codebase Usage
In production code, validation usually converts the value once and then uses the converted result. This avoids parsing the same text multiple times.
function parseFiniteNumber(value: string): number | undefined {
const trimmed = value.trim();
if (trimmed === "") {
return undefined;
}
const numberValue = Number(trimmed);
if (!Number.isFinite(numberValue)) {
return undefined;
}
return numberValue;
}
const price = parseFiniteNumber("24.99");
if (price === undefined) {
console.error("Enter a valid price.");
} else {
console.log(price * 1.2);
}
This is a guard clause pattern: invalid values return early, leaving the successful path simple.
Projects also often add business rules after numeric validation:
function parsePositiveInteger(value: string): number | undefined {
if (!/^\d+$/.test(value.trim())) {
return undefined;
}
const result = Number(value.trim());
return Number.isSafeInteger(result) && result > 0 ? result : undefined;
}
This is appropriate for values such as item counts, IDs, and page numbers, where decimals, signs, and scientific notation should be rejected.
Common Mistakes
Using parseFloat() as validation
// Broken for strict validation
const valid = !isNaN(parseFloat("12px")); // true
parseFloat() accepts a numeric prefix. Use Number() for whole-string conversion, or use a regular expression for a specific format.
Passing a string to global isNaN()
// TypeScript reports an error with standard typings
isNaN("42");
JavaScript may coerce values at runtime, but TypeScript correctly encourages you to be explicit. Convert first, or use Number.isFinite(Number(value)).
Forgetting empty strings
Number(""); // 0
Number(" "); // 0
Check value.trim() !== "" before conversion if blank input is invalid.
Comparing string lengths after parsing
// Fragile and incorrect for many valid representations
parseFloat("01").toString().length !== "01".length;
Different strings can represent the same number: "01", "1.0", "1e3", and " 1 ". Numeric conversion normalizes them, so length comparisons do not reliably validate input.
Accepting infinity accidentally
!Number.isNaN(Number("Infinity")); // true
If your application needs an ordinary finite number, use Number.isFinite() rather than only checking for NaN.
Using a loose equality check
// Avoid
if (value == "") {
// Some non-string values can behave unexpectedly.
}
When working with strings, use value.trim() === "" and strict equality.
Comparisons
| Tool or approach | Whole string required? | "12px" | Empty string | Best use |
|---|---|---|---|---|
parseFloat(value) | No | Parses as 12 | NaN | Reading a numeric prefix intentionally |
Number(value) | Yes | NaN | Converts to 0 | Converting a complete JavaScript numeric value |
Number.isNaN(value) | Not a converter | Not applicable | Not applicable | Testing whether an existing number is NaN |
Number.isFinite(Number(value)) | Yes | false | true without an extra blank check | Validating finite numeric values |
| Regular expression | You decide | Usually false | Usually false | Enforcing an exact text format |
isNaN() is the older global function. It coerces its argument at runtime, which can make its behavior surprising. Prefer Number.isNaN() when you already have a number, and Number.isFinite(Number(text)) when validating numeric text.
For integer-only fields, add Number.isInteger() or use an integer-specific regular expression:
const isIntegerString = (value: string) => /^[+-]?\d+$/.test(value.trim());
Cheat Sheet
// General finite numeric string
function isNumericString(value: string): boolean {
const trimmed = value.trim();
return trimmed !== "" && Number.isFinite(Number(trimmed));
}
// Decimal notation only: 12, -3.5, .25, 1e6
const isDecimalString = (value: string): boolean =>
/^[+-]?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?$/.test(value.trim());
// Positive whole-number text: 1, 25, 300
const isPositiveIntegerString = (value: string): boolean =>
/^[1-9]\d*$/.test(value.trim());
Rules to remember:
- Do not use
parseFloat()for strict validation. - Reject blank input before calling
Number(). - Use
Number.isFinite()to rejectNaNand infinities. - Use a regular expression when permitted formatting matters.
- Parse once and reuse the parsed number when possible.
FAQ
Why does parseFloat("9BX46B6A") return 9?
parseFloat() parses from the beginning of a string and stops at the first character that cannot be part of a floating-point number. It is not a full-string validator.
Why does TypeScript complain about isNaN("9")?
TypeScript's standard declaration expects a number for isNaN(). This helps prevent implicit conversion mistakes. Convert the string explicitly instead.
Is Number("12px") a number?
No. It returns NaN because Number() requires the entire trimmed string to be a valid numeric representation.
Does Number() accept spaces around a number?
Yes. Number(" 42 ") returns 42. Trim explicitly if you also need to reject whitespace-only input.
Does Number() accept hexadecimal values such as "0xFF"?
Yes, Number("0xFF") returns 255. Use a decimal regular expression if hexadecimal input should be rejected.
Should Infinity count as numeric?
It depends on the domain, but it is usually invalid for user-entered quantities, prices, and database values. Number.isFinite() rejects it.
How do I validate an integer instead of any number?
Use an integer regular expression such as /^[+-]?\d+$/, or convert using Number() and then check Number.isInteger(result).
Is a regex always necessary for numeric strings?
No. Number.isFinite(Number(value)) is enough when JavaScript's accepted number formats match your requirements. Use a regex only when you need a stricter textual format.
Mini Project
Description
Build a small TypeScript utility that validates an order quantity entered as text. Order quantities must be positive whole numbers, so values such as "3" are valid while "3.5", "3items", and blank text are invalid.
Goal
Create a function that converts a valid quantity string to a number and returns undefined for invalid input.
Requirements
Validate the entire input rather than only a numeric prefix. Reject empty or whitespace-only input. Accept only positive whole numbers. Reject decimal values, signs, letters, and zero. Return the parsed number when the input is valid.
Keep learning
Related questions
@Directive vs @Component in Angular: Differences, Use Cases, and When to Use Each
Learn the difference between @Directive and @Component in Angular, including use cases, examples, and when to choose each.
Accessing Input Value from EventTarget in TypeScript
Learn why EventTarget has no value property in TypeScript and safely read values from HTML input events in Angular applications.
Angular (change) vs (ngModelChange): What’s the Difference?
Learn the difference between Angular (change) and (ngModelChange), when each fires, and which one to use in forms and inputs.