Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Type guards for types of arrays

Using typeof type guards for non-array types is pretty straightforward (example from the docs):

function padLeft(value: string, padding: string | number) {
    if (typeof padding === "number") {
        return Array(padding + 1).join(" ") + value;
    }
    if (typeof padding === "string") {
        return padding + value;
    }
    throw new Error(`Expected string or number, got '${padding}'.`);
}

However, when it comes to array types it gets more complicated. I would assume that the following code would work:

function join(array: number[] | string[]): string {
    if (typeof array[0] === 'number') {
        return foo(array);
    } else {
        return bar(array)
    }
}

function foo(n: number[]): string {
    return n.join();
}

function bar(s: string[]): string {
    return s.join();
}

Seems quite simple to me: the expected type is either array of numbers or array of strings.
If the type of the first element in the array is number then the array is of type number[]. Otherwise, this is a strings array.

Unfortunately, TypeScript compiler is not that smart and I get a compilation error:

Argument of type 'number[] | string[]' is not assignable to parameter of type 'string[]'

How do I make it work?

like image 878
JeB Avatar asked Apr 13 '18 09:04

JeB


People also ask

What is a type guard function?

A type guard is a TypeScript technique used to get information about the type of a variable, usually within a conditional block. Type guards are regular functions that return a boolean, taking a type and telling TypeScript if it can be narrowed down to something more specific.

What is type alias in TypeScript?

In Typescript, Type aliases give a type a new name. They are similar to interfaces in that they can be used to name primitives and any other kinds that you'd have to define by hand otherwise. Aliasing doesn't truly create a new type; instead, it gives that type a new name.

What is type assertion in TypeScript?

In Typescript, Type assertion is a technique that informs the compiler about the type of a variable. Type assertion is similar to typecasting but it doesn't reconstruct code. You can use type assertion to specify a value's type and tell the compiler not to deduce it.

How do you check if an object is an array in TypeScript?

To check if a value is an array of specific type in TypeScript: Use the Array. isArray() method to check if the value is an array.


2 Answers

The answer is User-Defined Type Guards.

You can define a type guard of your own that will determine whether the array is array of numbers or not:

function isNumbers(array: number[] | string[]): array is number[] {
    return typeof array[0] === 'number'
}

Read more about user defined type guards here.

The following is a working example of your code:

function join(array: number[] | string[]): string {
    if (isNumbers(array)) {
        return foo(array);
    } else {
        return bar(array)
    }
}

function foo(n: number[]): string {
    return n.join();
}

function bar(s: string[]): string {
    return s.join();
}

function isNumbers(array: number[] | string[]): array is number[] {
    return typeof array[0] === 'number'
}

TypeScript playground example.

like image 85
JeB Avatar answered Nov 15 '22 08:11

JeB


JeB's answer was a very good start for me but I needed something a little more robust because I have to deal with arbitrary input. Here's what I came up with:

function isNumberArray(value : unknown) : value is number[] {
    if (!Array.isArray(value)) {
        return false;
    }

    if (value.some((v) => typeof v !== "number")) {
        return false;
    }

    return true;
}
like image 33
Pier-Luc Gendreau Avatar answered Nov 15 '22 08:11

Pier-Luc Gendreau