Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does TypeScript infer the 'never' type when reducing an Array with concat?

Code speaks better than language, so:

['a', 'b', 'c'].reduce((accumulator, value) => accumulator.concat(value), []); 

The code is very silly and returns a copied Array...

TS complains on concat's argument: TS2345: Argument of type 'string' is not assignable to parameter of type 'ConcatArray'.

like image 461
millsp Avatar asked Jan 09 '19 19:01

millsp


People also ask

How do I fix type string is not assignable to type never?

The error "Type is not assignable to type 'never'" occurs when we declare an empty array without explicitly typing it and attempt to mutate the array. To solve the error, explicitly type the empty array, e.g. const arr: string[] = []; .

What is never array?

This type represents an array that will never contain any elements (will always be empty). To solve the error, explicitly type the empty array.


2 Answers

I believe this is because the type for [] is inferred to be never[], which is the type for an array that MUST be empty. You can use a type cast to address this:

['a', 'b', 'c'].reduce((accumulator, value) => accumulator.concat(value), [] as string[]); 

Normally this wouldn't be much of a problem since TypeScript does a decent job at figuring out a better type to assign to an empty array based on what you do with it. However, since your example is 'silly' as you put it, TypeScript isn't able to make any inferences and leaves the type as never[].

like image 99
Matt H Avatar answered Oct 16 '22 22:10

Matt H


A better solution which avoids a type assertion (aka type cast) in two variants:

  1. Use string[] as the generic type parameter of the reduce method (thanks @depoulo for mentioning it):
['a', 'b', 'c'].reduce<string[]>((accumulator, value) => accumulator.concat(value), []); 
  1. Type the accumulator value as string[] (and avoid a type cast on []):
['a', 'b', 'c'].reduce((accumulator: string[], value) => accumulator.concat(value), []); 

Play with this solution in the typescript playground.

Notes:

  1. Type assertions (sometimes called type casts) should be avoided if you can because you're taking one type and transpose it onto something else. This can cause side-effects since you're manually taking control of coercing a variable into another type.

  2. This typescript error only occurs if the strictNullChecks option is set to true. The Typescript error disappears when disabling that option, but that is probably not what you want.

  3. I reference the entire error message I get with Typescript 3.9.2 here so that Google finds this thread for people who are searching for answers (because Typescript error messages sometimes change from version to version):

    No overload matches this call.   Overload 1 of 2, '(...items: ConcatArray<never>[]): never[]', gave the following error.  Argument of type 'string' is not assignable to parameter of type 'ConcatArray<never>'.   Overload 2 of 2, '(...items: ConcatArray<never>[]): never[]', gave the following error.  Argument of type 'string' is not assignable to parameter of type 'ConcatArray<never>'.(2769) 
like image 40
Andru Avatar answered Oct 16 '22 20:10

Andru