Understanding `is number` in TypeScript

TypeScript is a statically typed superset of JavaScript that adds optional types to the language. One of the powerful features in TypeScript is type guards, which allow you to narrow down the type of a variable within a certain scope. The is number type predicate is a specific type guard that helps in determining if a variable is of the number type. This blog post will delve into the fundamental concepts, usage methods, common practices, and best practices related to is number in TypeScript.

Table of Contents#

  1. Fundamental Concepts
  2. Usage Methods
  3. Common Practices
  4. Best Practices
  5. Conclusion
  6. References

Fundamental Concepts#

Type Guards#

Type guards are expressions that perform a runtime check that guarantees the type in a certain scope. They are used to narrow down the type of a variable, which can be especially useful when dealing with union types.

is number Type Predicate#

The is number type predicate is a custom type guard that returns a boolean value indicating whether a given variable is of the number type. It is typically used in a function that takes a value of a union type and returns a boolean. If the return value is true, TypeScript will narrow down the type of the variable to number within the scope where the type guard is used.

Here is a simple example of a type guard using is number:

function isNumber(value: any): value is number {
    return typeof value === 'number';
}

In this example, the isNumber function takes a value of type any and returns true if the value is of type number and false otherwise. The value is number syntax is the type predicate that tells TypeScript to narrow down the type of value to number if the function returns true.

Usage Methods#

Using in Conditional Statements#

One of the most common ways to use the is number type guard is in conditional statements. Here is an example:

function printValue(value: string | number) {
    if (isNumber(value)) {
        // Inside this block, TypeScript knows that value is a number
        console.log(`The number is: ${value.toFixed(2)}`);
    } else {
        // Here, TypeScript knows that value is a string
        console.log(`The string is: ${value.toUpperCase()}`);
    }
}
 
printValue(123.456); 
printValue('hello'); 

In this example, the printValue function takes a value of type string | number. Inside the if statement, the isNumber type guard is used to check if the value is a number. If it is, TypeScript narrows down the type of value to number and allows us to call the toFixed method. Otherwise, it knows that value is a string and allows us to call the toUpperCase method.

Using in Function Return Types#

The is number type guard can also be used to narrow down the return type of a function. Here is an example:

function getNumberOrString(value: string | number): number | null {
    if (isNumber(value)) {
        return value;
    }
    return null;
}
 
const result = getNumberOrString(42);
if (result!== null) {
    console.log(`The number is: ${result}`);
}

In this example, the getNumberOrString function takes a value of type string | number and returns a number if the input is a number, or null otherwise. The isNumber type guard is used to determine if the input is a number and return it accordingly.

Common Practices#

Reusable Type Guards#

It is a good practice to create reusable type guards. For example, you can create a utility file with all your type guards and import them as needed. Here is an example of a utility file:

// typeGuards.ts
export function isNumber(value: any): value is number {
    return typeof value === 'number';
}
 
// main.ts
import { isNumber } from './typeGuards';
 
function processValue(value: string | number) {
    if (isNumber(value)) {
        // Do something with the number
    }
}

This way, you can reuse the isNumber type guard in multiple places in your application.

Combining Type Guards#

You can also combine multiple type guards to create more complex type checks. Here is an example:

function isPositiveNumber(value: any): value is number {
    return isNumber(value) && value > 0;
}
 
function printPositiveNumber(value: string | number) {
    if (isPositiveNumber(value)) {
        console.log(`The positive number is: ${value}`);
    }
}

In this example, the isPositiveNumber type guard combines the isNumber type guard with a check to see if the number is positive.

Best Practices#

Avoiding any Type#

While the isNumber function in our examples takes a parameter of type any, it is generally a good practice to avoid using the any type as much as possible. Instead, use more specific types. For example, if you know that the value can only be a string or a number, you can change the function signature to:

function isNumber(value: string | number): value is number {
    return typeof value === 'number';
}

This way, TypeScript can provide more type safety and better error messages.

Testing Type Guards#

It is important to test your type guards to ensure they work as expected. You can use a testing framework like Jest to write unit tests for your type guards. Here is an example of a test for the isNumber type guard:

import { isNumber } from './typeGuards';
 
describe('isNumber', () => {
    test('returns true for numbers', () => {
        expect(isNumber(123)).toBe(true);
        expect(isNumber(0)).toBe(true);
        expect(isNumber(-123)).toBe(true);
    });
 
    test('returns false for non-numbers', () => {
        expect(isNumber('hello')).toBe(false);
        expect(isNumber(null)).toBe(false);
        expect(isNumber(undefined)).toBe(false);
    });
});

Conclusion#

The is number type guard in TypeScript is a powerful tool for narrowing down the type of a variable and ensuring type safety in your code. By understanding the fundamental concepts, usage methods, common practices, and best practices, you can use it effectively in your TypeScript projects. Remember to create reusable type guards, avoid using the any type, and test your type guards to ensure they work as expected.

References#