3

I have a piece of code where I need to know in a few different places whether or not a variable is an array (it can be an array or string). If it is, I map over the data. If not, I do something else.

Simplified code, which typescript complains about:

function myFunc(input: unknown[] | string) {

  const isArray = Array.isArray(input);
  
  if (isArray) {
    // this line throws an error, since `map` is not a valid method for strings
    return input.map(/* do stuff here*/);
  }

  return input;

}

If I change the line with the if statement to this:

  if (Array.isArray(input))

then typescript seems to understand that input is in fact an array.

I use that check in several places in the real code though, and would like to use a single variable for readability, rather than doing Array.isArray every time.

What's going on here?

2 Answers 2

4

Typescript 4.4+

What you want do does work in Typescript 4.4 (unreleased as of July 6, 2021)

See blog post detailing aliased conditions

Playground using nightly build


Typescript 4.3

In earlier versions of typescript your options are a bit more limited. The problem is that typescript is not smart enough to understand that const isArray is derived from Array.isArray(input). After that line, it's just a boolean value with no connection to any other value.

I would probably create an intermediate variable that only has a value if the input is an array. That way typescript can keep track of that type on one single variable.

Something like:

function myFunc(input: unknown[] | string) {
  const inputArray = Array.isArray(input) ? input : null

  if (inputArray) {
    return inputArray.map(str => str);
  }

  return input;
}

Playground

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

2 Comments

Your answer is innovative and works, but you have manipulated the variable to make it have either a falsy value or an array. From whatever I tried, any type check not inside the if-else body does not work. Can we say that with sureity?
Yes, it works, for sure. There is still a typecheck in the if. inputArray is type string[] | null. Then it's just a simple refinement. The if (inputArray) will filter out the null from that union, leaving you with just string[] in the conditional branch.
1

An Array.isArray is a special check that narrows the variable type. There is whole chapter on this: https://www.typescriptlang.org/docs/handbook/2/narrowing.html

Maybe you want to use pair of variables and use them later in the function.

const inputArray = Array.isArray(input) ? input : null;
const inputString = Array.isArray(input) ? null : input;

Now you can use corresponding variable if not null. But this only transforms one form of check to another form, so IMHO not so useful, really.

Of course, you can use one variable as any type, but you will loose all TS goodness.

const inputAny: any = input;

1 Comment

You could replace Array.isArray with const isArray = input instanceof Array; and it would still not work.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.