0

I created the following Typescript extension to convert a string to Number:

declare global {
  interface String {
    toNumber(): number | null;
  }
}

String.prototype.toNumber = function(this: string) {
  return parseFloat(this);
}

When it is not possible to parse the string to number either because it is invalid, null, undefined, etc I would always like to return null.

How can I do this?

Miguel Moura
  • 36,732
  • 85
  • 259
  • 481
  • I would strongly suggest using `Number()` instead of `parseFloat()`. The latter will return a number from the string "123hello world". – Pointy Aug 30 '21 at 22:47
  • But, for example, Number(“ “) will return 0 and I would like to return null. I think Number in some cases converts to Zero. I am not sure which ones. I have been looking for it ... – Miguel Moura Aug 30 '21 at 22:54
  • 1
    Check the return value from `Number()` before returning. `Number()` will convert any string that actually represents zero to the numeric value 0, but not any others. The `parseFloat()` API was designed for parsing style values like "12.5px", it ignores the "px". It should not be used generally when you really want to validate the entire source string. And, on top of that, a simple unary `+` operation is the same as explicitly calling `Number()`, but many think that `Number()` is clearer. – Pointy Aug 30 '21 at 23:00
  • I would suggest a regex that checks for if the string only contains numbers. There are a number of options in this question: https://stackoverflow.com/questions/9011524/regex-to-check-whether-a-string-contains-only-numbers – zecuria Aug 30 '21 at 23:01
  • 1
    @zecuria It's not clear if OP wants to accept all JS numberic literals like `0b101`, `1e5`, etc. (or [numeric separators](https://github.com/tc39/proposal-numeric-separator)). I think the scope needs to be better-defined. – jsejcksn Aug 31 '21 at 00:06
  • @jsejcksn yep definitely agree, parsing numbers is not an easy problem and OP really needs to understand the exact constraints of each approach – zecuria Aug 31 '21 at 00:12

3 Answers3

3

I am assuming you already understand the differences between parseFloat / Number as conversion mechanisms.

Effectively all you need to do is check if the output is NaN. You can do this by:

String.prototype.toNumber = function(this: string) {
  const num = parseFloat(this);
  return Number.isNaN(num) ? null : num;
}
zecuria
  • 728
  • 4
  • 9
0

A simple answer would be to use return Number(this) || null;

The Number function will convert to a number or NaN, NaN || null will return null (because NaN is falsey).

Updated added testing for zero condition, which with the above code would have also returned null. If that is not what you want, this code will allow zero to return. (Note that this can be done many different ways!):

const parsedValue = Number(this);

return parsedValue === 0 ? parsedValue : parsedValue || null;

Updated to use the parseFloat function, example of early exit for string of '0'. Very similar to the previous updated example.

if (this === '0') {
  return 0;
}

return parseFloat(this) || null;
ps2goat
  • 8,067
  • 1
  • 35
  • 68
  • 3
    Got to watch out for zero however. It's also falsy. – Pointy Aug 30 '21 at 22:45
  • @Pointy Yes, for example, Number(“ “) converts to 0 which I want to avoid that is why I used parseFloat. How to avoid such cases with Number? – Miguel Moura Aug 30 '21 at 22:57
  • @MiguelMoura what do you imagine that `parseFloat("0")` returns? – Pointy Aug 30 '21 at 23:01
  • Would return 0? – Miguel Moura Aug 30 '21 at 23:31
  • Updated, thanks for pointing that out. I'm used to cases where things have to be greater than zero, e.g., ORM IDs. @MiguelMoura, you'll have to add your additional test cases to the logic. If an empty string should return null, test for that case before trying to parse the number. If you're avoiding zero altogether, my original code would work for you. So yes, you can use parseFloat if you want that functionality. – ps2goat Aug 31 '21 at 03:59
0

If you want to return either a non-zero valid number (well, note that NaN is a number, but I think I know what you mean), then check for what you don't want before returning:

Object.defineProperty(String.prototype, "toNumber", {
  value: function(str) {
    let num = Number(str);
    return num === 0 || isNaN(num) ? null : num;
  }
});

(Defining properties directly on the prototype is a bad habit and can lead to weird behavior; using .defineProperty gives you a property that is not enumerable.)

Oh, and that's JavaScript, obviously, not Typescript.

Pointy
  • 405,095
  • 59
  • 585
  • 614
  • Maybe I did not explain it well. If the string is “0” then O want to return 0. But if the string is empty then I want to return null and not zero. In this situation and in a few others there is a difference between using Number or ParseFlloat – Miguel Moura Aug 30 '21 at 23:38
  • @MiguelMoura yes, clear now. Well, you could `.trim()` the string before calling `Number()` after explicitly checking for `""`. – Pointy Aug 30 '21 at 23:55