193

How can I test if a variable is an array of string in TypeScript? Something like this:

function f(): string {
    var a: string[] = ["A", "B", "C"];

    if (typeof a === "string[]")    {
        return "Yes"
    }
    else {
        // returns no as it's 'object'
        return "No"
    }
};

TypeScript.io here: http://typescript.io/k0ZiJzso0Qg/2

Edit: I've updated the text to ask for a test for string[]. This was only in the code example previously.

Sean Kearon
  • 10,987
  • 13
  • 77
  • 93
  • possible duplicate of [How do you check if a variable is an array in JavaScript?](http://stackoverflow.com/questions/767486/how-do-you-check-if-a-variable-is-an-array-in-javascript) – WiredPrairie Apr 17 '14 at 10:48
  • 1
    As TypeScript just compiles to JavaScript, the answers may be found by searching for a JavaScript solution. Further, it's worth it to see some of the answers as the answer depends on the host and how it's being used and passed. – WiredPrairie Apr 17 '14 at 10:50

8 Answers8

275

You cannot test for string[] in the general case but you can test for Array quite easily the same as in JavaScript https://stackoverflow.com/a/767492/390330 (I prefer Array.isArray(value)).

If you specifically want for string array you can do something like:

if (Array.isArray(value)) {
   var somethingIsNotString = false;
   value.forEach(function(item){
      if(typeof item !== 'string'){
         somethingIsNotString = true;
      }
   })
   if(!somethingIsNotString && value.length > 0){
      console.log('string[]!');
   }
}

In case you need to check for an array of a class (not a basic type)

if(items && (items.length > 0) && (items[0] instanceof MyClassName))

If you are not sure that all items are same type

items.every(it => it instanceof MyClassName)
Mauricio Gracia Gutierrez
  • 10,288
  • 6
  • 68
  • 99
basarat
  • 261,912
  • 58
  • 460
  • 511
  • 3
    `instanceof Array` fails when checking arrays across frames. Prefer `Array.isArray`: https://twitter.com/mgechev/status/1292709820873748480 – axmrnv Aug 12 '20 at 19:24
  • @axmrnv Completely agree – basarat Aug 13 '20 at 01:31
  • also, you can break the loop right after telling that somethingIsNotString. With your code, it will necessarily loop through all entire array, which is not necessary. – Kukuster Oct 12 '20 at 15:12
  • @Kukuster value.some() coul be used then, but the answer is related to how to determine array/not-array not about anything else – Mauricio Gracia Gutierrez Aug 23 '21 at 15:04
74

Another option is Array.isArray()

if(! Array.isArray(classNames) ){
    classNames = [classNames]
}
daremachine
  • 2,678
  • 2
  • 23
  • 34
grigson
  • 3,458
  • 29
  • 20
  • 12
    But this doesn't check for the elements being of type `string`. – User Jan 08 '19 at 11:13
  • 8
    Yeap. Then you need one additional checking. `Array.isArray(classNames) && classNames.every(it => typeof it === 'string')` – grigson Jan 09 '19 at 15:21
  • 1
    @grigson how about performance when checking 400+ results? Should I disconsider this kind of type check, check just the first child or is it safe tu use this "every" thing? – giovannipds Jan 17 '19 at 11:35
  • 1
    @giovannipds Please check [asmironov's answer](https://stackoverflow.com/a/50523378/3136474). – Dinei May 08 '19 at 15:03
43

Here is the most concise solution so far:

function isArrayOfStrings(value: unknown): value is string[] {
   return Array.isArray(value) && value.every(item => typeof item === "string");
}

Note that value.every returns true for an empty array. To return false for an empty array, add value.length > 0 to the condition clause:

function isNonEmptyArrayOfStrings(value: unknown): value is string[] {
    return Array.isArray(value) && value.length > 0 && value.every(item => typeof item === "string");
}

There is no any run-time type information in TypeScript (and there won't be, see TypeScript Design Goals > Non goals, 5), so there is no way to get the "type" of elements of an empty array. For non-empty array all you can do is to check the type of its items, one by one.

Type predicate value is string[] narrows type of value to string[] in the corresponding scope. This is a TypeScript feature, the real return type of the function is boolean.

axmrnv
  • 964
  • 10
  • 23
  • 1
    Please note if an Array is empty you need to check that value.length>0 and not just value.length as this will return 0 – markyph Feb 25 '21 at 09:13
  • @markyph, 0 is a falsy value in JS/TS and is interpreted as false in this context. – axmrnv Feb 25 '21 at 12:27
  • 1
    Yes am aware 0 is falsy - but the above broke my test as expected it to be "false" but returned 0 - value.length>0 guarantees it to be false – markyph Feb 26 '21 at 13:43
  • @markyph That's interesting. Probable due to short-circuit implementation in JS. I imagine it's something like this: for each expression in the `&&` chain, if it's truthy, then go to the next. But if it is falsy, then return it as it is. Return true if all terms in the chain are exhausted. You can probably use the double exclamation mark convention to coerce a truthy value to a boolean i.e. `!!value.length`. To me that's more readable as it shows intention and it only costs two characters. And it seems in this case it's also more correct. Or arguably the above `value.length>0` is clearer. – Colm Bhandal Jul 04 '22 at 16:41
  • 1
    Probably you can also use the "is string[]" as a type predicate. See https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates – brutuscat Aug 10 '22 at 12:32
8

I know this has been answered, but TypeScript introduced type guards: https://www.typescriptlang.org/docs/handbook/advanced-types.html#typeof-type-guards

If you have a type like: Object[] | string[] and what to do something conditionally based on what type it is - you can use this type guarding:

function isStringArray(value: any): value is string[] {
  if (value instanceof Array) {
    for (const item of value) {
      if (typeof item !== 'string') {
        return false
      }
    }
    return true
  }
  return false
}

function join<T>(value: string[] | T[]) {
  if (isStringArray(value)) {
    return value.join(',') // value is string[] here
  } else {
    return value.map((x) => x.toString()).join(',') // value is T[] here
  }
}

There is an issue with an empty array being typed as string[], but that might be okay

Dai
  • 141,631
  • 28
  • 261
  • 374
Nicholas Boll
  • 845
  • 7
  • 9
6

Try this:

if (value instanceof Array) {
alert('value is Array!');
} else {
alert('Not an array');
}
Tcanarchy
  • 760
  • 1
  • 8
  • 20
5

You can have do it easily using Array.prototype.some() as below.

const isStringArray = (test: any[]): boolean => {
 return Array.isArray(test) && !test.some((value) => typeof value !== 'string')
}
const myArray = ["A", "B", "C"]
console.log(isStringArray(myArray)) // will be log true if string array

I believe this approach is better that others. That is why I am posting this answer.

Update on Sebastian Vittersø's comment

Here you can use Array.prototype.every() as well.

const isStringArray = (test: any[]): boolean => {
 return Array.isArray(test) && test.every((value) => typeof value === 'string')
}
Sudarshana Dayananda
  • 5,165
  • 2
  • 23
  • 45
1

there is a little problem here because the

if (typeof item !== 'string') {
    return false
}

will not stop the foreach. So the function will return true even if the array does contain none string values.

This seems to wok for me:

function isStringArray(value: any): value is number[] {
  if (Object.prototype.toString.call(value) === '[object Array]') {
     if (value.length < 1) {
       return false;
     } else {
       return value.every((d: any) => typeof d === 'string');
     }
  }
  return false;
}

Greetings, Hans

hans
  • 1,001
  • 1
  • 11
  • 14
1

Referring to the type-guards (https://www.typescriptlang.org/docs/handbook/advanced-types.html#typeof-type-guards), you can do something like ->

/**
 * type-guard function to check at run-time if all the elements inside
 * from the array are strings
 * @param array
 * @returns - boolean, true if the array is string array
 */
export function isStringArray(array: unknown[]): array is string[] {
  return array.every((element) => typeof element === 'string')
}