1

TL;DR

function foo(x: string[] | number[]) {

    // HOW TO DETERMINE WHETHER x IS OF TYPE string[] OR number[]?

}

Full Story

I have a class X which can be instantiated with either no args or an array of string or an array of number:

class X {

    constructor(...args?: string[] | number[]) {

        // ensure there are args and array isn't empty
        if (!args || args.length === 0) {
            return;
        }

        // check for number[]
        if (typeof args[0] === 'number') { // <<<< THIS IS WHERE I'M LOOKING FOR A DIFFERENT SOLUTION
            this.setNumbers(...args); // <<<< THE ERROR

        }
        // it's a string[]
        else {
            this.setStrings(...args);
        }
    }

    function setNumbers(...args: number[]): X {
        // TODO: validate and set numbers
        return this;
    }

    function setStrings(...args: string[]): X {
        // TODO: validate and set strings
        return this;
    }
}

I couldn't find a solution on how to determine the type of the args array, other than checking the type of the actual content.

And logically speaking this would be a valid solution: The args array can only ever be either of type string[] or number[]. It can never be a mix of both types (like a (string | number)[] would allow it to be).
So if it's non-empty and has an element of type number, then it has to be of type number[], but TypeScript doesn't agree with me on that:

TS2345: Argument of type 'string | number' is not assignable to parameter of type 'number'.

Ideas to a Solution

It would be nice if we could check if (args instanceof number[]), but that won't work on primitive data types, because they can't be instantiated and what gets instantiated here is an array of a certain type (not the type itself) and TypeScript kinda agrees: TS2693: 'number' only refers to a type, but is being used as a value here.

So how about if (args instanceof Array<number>)? Would be nice as well, but TS2359: The right-hand side of an 'instanceof' expression must be of type 'any' or of a type assignable to the 'Function' interface type..

What would be the correct way to check the type of the args array?

1 Answer 1

1

There is no runtime information on the Array itself, so checking the content is a good approach. You need to use a user-defined type guard to convince the compiler that your check narrows the type for the entire array:

class X {

    constructor(...args: string[] | number[]) {

        // ensure there are arguments and array isn't empty
        if (!args || args.length === 0) {
            return;
        }

        // check for number[]
        if (this.isNumberArray(arguments[0])) { 
            this.setNumbers(...arguments);

        }
        // it's a string[]
        else {
            this.setStrings(...arguments);
        }
    }

    isNumberArray(args: string[] | number[]): args is number[] {
      return typeof arguments[0] === 'number';
    }

    setNumbers(...args: number[]): X {
        // TODO: validate and set numbers
        return this;
    }

    setStrings(...args: string[]): X {
        // TODO: validate and set strings
        return this;
    }
}

Playground link

On top of that:

The fact that arguments object is used for functions with variable number of parameters in ES3 & ES5 should not make you use it in typescript.

For functions with variable number of parameters in typescript, you should use rest parameters. Typescript compiler will compile it to:

Sign up to request clarification or add additional context in comments.

2 Comments

Thank you! That's exactly what I was looking for! You are right, I shouldn't have used arguments and in my actual code I don't. I've edited my question and replaced arguments with args to not confuse other. Regarding rest parameters: I'm using those in my code, am I not?
You are, but the compiler gets confused with arguments param name. Simple rename to args did the job

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.