How to convert a number to a string in TypeScript

arahansen

Andrew Hansen

Posted on January 20, 2022

How to convert a number to a string in TypeScript

You're writing a TypeScript application and you hit an error like this:

Argument of type 'string' is not assignable to parameter of type 'number'.(2345)
Enter fullscreen mode Exit fullscreen mode

This could happen for a number of reasons. For example, I often run into this when reading a value from an input field. I intend the user to input a number in a form field field but the value I get back is a string (even when the input is type="number"!).

Here's an example:

// user input value
const birthYear: string = '12345'

function isMillenial(birthYear: number) {
    return birthYear < 1980 || birthYear > 2000
}

const millenial = isMillenial(birthYear /* error! */)
Enter fullscreen mode Exit fullscreen mode

To fix this I need to add a conversion that converts a string into a number. This is known as typecasting, or type coercion.

TypeScript is a superset of JavaScript which means all JavaScript applications are also valid TypeScript applications. This means the most straight forward way to fix this is to use methods that JavaScript provides us.

To coerce a string into a number, we can wrap our value in one of several functions: parseInt, parseFloat, or Number (this stackoverflow answer has a great breakdown of the differences).

For our birthYear example, we want parseInt:

const birthYear: string = '12345'
const birthYearNum: number = parseInt(birthYear)
Enter fullscreen mode Exit fullscreen mode

TypeScript has a built-in type definition for parseInt which you can inspect in many IDEs by hovering over the function call. It looks like this:

function parseInt(string: string, radix?: number | undefined): number
Enter fullscreen mode Exit fullscreen mode

Notably the return type is always a number. If we update our example, the error goes away!

// user input value
const birthYear: string = '12345'
const birthYearNum: number = parseInt(birthYear)

function isMillenial(birthYear: number) {
    return birthYear > 1980 || birthYear < 2000
}

const millenial = isMillenial(birthYearNum /* no more error! */)
Enter fullscreen mode Exit fullscreen mode

Error handling

When dealing with user input we should assume we could receive any string. It's possible we receive something that can't be parsed into a valid number:

const birthYear: string = 'abcd'
const birthYearNum: number = parseInt(birthYear) // NaN!
Enter fullscreen mode Exit fullscreen mode

Here parseInt will not throw an error but return a value of NaN (or, Not a Number). In TypeScript (and JavaScript) typeof NaN === 'number'. So, even though we are dealing with an invalid input TypeScript will not give us a type error. Our code will need to handle this explicitly which we can do with the Number.isNaN function:

const birthYear: string = 'abcd'
const birthYearNum: number = parseInt(birthYear) // NaN!

if (Number.isNaN(birthYearNum)) {
    throw new Error('Invalid birthYear!')
    // or, show an error to the user
}
Enter fullscreen mode Exit fullscreen mode

A caution on using TypeScript type assertions

TypeScript provides a (seemingly) helpful feature with the as keyword. This feature is known as type assertion.

Rather than using something like parseInt you might be tempted to do the following:

const millenial = isMillenial(birthYear as number)
Enter fullscreen mode Exit fullscreen mode

TypeScript will complain about this and suggest a workaround:

Conversion of type 'string' to type 'number' may be a mistake
because neither type sufficiently overlaps with the other. 
If this was intentional, convert the expression to 
'unknown' first. (2352)
Enter fullscreen mode Exit fullscreen mode

Which would reasonably lead you to implement that workaround:

const millenial = isMillenial((birthYear as unknown) as number)
Enter fullscreen mode Exit fullscreen mode

However, it's important to keep in mind that TypeScript compiles down to JavaScript to produce an executable program. In doing so, all type-related information is compiled away. You're effectively left with this:

const millenial = isMillenial(birthYear)
Enter fullscreen mode Exit fullscreen mode

Even though we used as to tell TypeScript to treat birthYear as a number, the behavior of our program doesn't actually change. All you've really done is tell TypeScript: "I know what I'm doing better than you do."

There are some complex scenarios where this might be useful (which we will save for another day). However, you should strive to produce the correct type using proper runtime type conversion methods (like parseInt) whenever possible. Otherwise you are opting out of the biggest benefits of TypeScript.

💖 💪 🙅 🚩
arahansen
Andrew Hansen

Posted on January 20, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related