Lessons from open-source: Typesafe “getInt” utility function in Next.js source code

Lessons from open-source: Typesafe “getInt” utility function in Next.js source code

This lesson is picked from Next.js source code. In this article, you will learn what a high quality getInt util function looks like, especially the edge case to handle infinite numbers.

getInt

You will find the following at shared/lib/get-img-props.ts

function getInt(x: unknown): number | undefined {

  if (typeof x === 'undefined') {
    return x
  }

  if (typeof x === 'number') {
    return Number.isFinite(x) ? x : NaN
  }

  if (typeof x === 'string' && /^[0–9]+$/.test(x)) {
    return parseInt(x, 10)
  }

  return NaN
}

I normally just use parseInt and do not bother much about handling cases when dealing with converting a string to integer. Reading the code that uses this function within the file, I understand that getInt is used to convert the width, height passed as props to an image element into an integer.

Practice the exercises based on documentation to become an expert in Next.js

What makes it high quality?

The code snippet above checks and handles the following cases:

  1. If the typeof x is undefined, it simply returns x

  2. If the type of x is number, it still checks if the number is finite to avoid the scenarios where the value is like 5/0

  3. If the typeof x is string, it validates that x only contains numbers before performing the parseInt

  4. Finally, it returns NaN

I have seen some proper usage of regular expressions wherever possible in the Next.js source code. This is a gentle reminder to rely on regex when you deal with matching certain files/folders, string matches etc., I tend to use includes, contains etc.,

What ChatGPT has to say about this piece of code?

I asked ChatGPT this question “why not just do parseInt(x) instead of using this function”.

Its response is as below:

The function parseInt(x) would indeed be simpler in terms of code length, but it doesn’t handle certain cases well. Here are some reasons why the original code might be preferable:

1. Type Safety: By explicitly checking the type of x and returning number | undefined, the function provides more clarity about what type of input it expects and what type of output it produces. This can help prevent unexpected behaviour.

2. Undefined Handling: The function checks if x is undefined and returns it as such. This behaviour is consistent with TypeScript’s type system, where a function with a return type of number | undefined explicitly indicates that it may return undefined under certain conditions.

3. Finite Number Check: The function checks if the number is finite before returning it. This prevents unexpected behaviour when parseInt() returns NaN for non-numeric inputs or inputs that cannot be parsed as integers.

4. Type Checking: The function performs type checking to ensure that x is either undefined, a number, or a string containing only digits. This helps ensure that the function behaves predictably and handles different types of inputs appropriately.

In summary, while parseInt(x) might work in some cases, the original function provides more robustness and clarity in handling various input scenarios, especially in a TypeScript context where type safety is emphasised.

Type Safety and Type Check made me realise no wonder it has got all such defensive checks against the input parameter to convert it to integer.

Conclusion:

“getInt” has type safety and type checking to handle undefined, number, string input parameter types. It is also worth noting to rely on regular expressions when possible.