How to convert a number to a string in TypeScript
Tags: typescript
Published: 2022-01-20 17:27:08

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)
``` 

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:

```ts
// user input value
const birthYear: string = '12345'
    
function isMillenial(birthYear: number) {
    return birthYear < 1980 || birthYear > 2000
}
    
const millenial = isMillenial(birthYear /* error! */)
```

To fix this I need to add a conversion that converts a string into a number. This is known as [typecasting, or type coercion](https://developer.mozilla.org/en-US/docs/Glossary/Type_Conversion).

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`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseFloat), [`parseFloat`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseInt), or [`Number`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/Number) ([this stackoverflow answer](https://stackoverflow.com/a/17106702) has a great breakdown of the differences). 

For our `birthYear` example, we want `parseInt`:

```ts
const birthYear: string = '12345'
const birthYearNum: number = parseInt(birthYear)
```

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:

```ts
function parseInt(string: string, radix?: number | undefined): number
```

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

```ts
// 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! */)
```    

## 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:

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

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:

```ts
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
}
```    

## A caution on using TypeScript type assertions

TypeScript provides a (seemingly) helpful feature with the `as` keyword. This feature is known as [type assertion](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#type-assertions). 

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

```ts
const millenial = isMillenial(birthYear as number)
```
    

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)
```    

Which would reasonably lead you to implement that workaround:

```ts
const millenial = isMillenial((birthYear as unknown) as number)
```
    

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:

```ts
const millenial = isMillenial(birthYear)
```

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.