I want to flatten string[][] into string[].

The advice given in dozens of SO answers is: [].concat(...arrays).

But that gives me this error:

Argument of type 'string[]' is not assignable to parameter of type 'ConcatArray'.
Types of property 'slice' are incompatible.
Type '(start?: number | undefined, end?: number | undefined) => string[]' is not assignable to type '(start?: number | undefined, end?: number | undefined) => never[]'.
Type 'string[]' is not assignable to type 'never[]'.
Type 'string' is not assignable to type 'never'.

Another way I tried is this:

let foo: string[][] = [["a", "b", "c"], ["a", "b", "c"], ["a", "b", "c"]];let bar = [].concat(...foo);

Which gives a similar error:

Argument of type 'string[]' is not assignable to parameter of type 'ConcatArray'.

Why does it work for everyone but me?

9

Best Answer


Try this:

const a = [["a", "b", "c"], ["a", "b", "c"], ["a", "b", "c"]]const result = a.reduce((accumulator, value) => accumulator.concat(value), []);console.log(result)

You can flatten the array with flat()

let foo: string[][] = [["a", "b", "c"], ["a", "b", "c"], ["a", "b", "c"]];let bar = foo.flat()

log

console.log(bar) // a,b,c,a,b,c,a,b,c 

UPDATE

By correcting the type to string[] you can also use concat

let foo: string[][] = [["a", "b", "c"], ["a", "b", "c"], ["a", "b", "c"]];let bar : string[] = []bar = bar.concat(foo[0], foo[1], foo[2])

Here's the simplest option:

let bar = ([] as string[]).concat(...foo);

Like @Kokodoko's approach but with the typings inline.

Here's a generic solution:

function flatten<T>(arr: T[][]): T[] {return ([] as T[]).concat(...arr);}

And a deep/recursive extension:

type NestedArray<T> = Array<NestedArray<T> | T>;function flattenDeep<T>(input: NestedArray<T>): T[] {return flatten(input.map(x => Array.isArray(x) ? flattenDeep(x) : [x]));};

This answer may be more efficient for a deep flatten, I just had fun trying to write something that felt more elegant to me.

.flat() will also give the type error. You can use Generics to solve this

let st : string[][] | Array<string> = [['a'] , ['b']]let bar = [].concat(...st);console.log(bar)

Either way, your call. Just know that your type declaration is not right.

The code

const res = [].concat(...foo);

should work. I guess it's a misconfiguration in tsconfig that causes that error for you. Make sure that there is at least es2015 (better es2018) in your tsconfig's lib array. To make the new flat work as shown by kokodoko, make sure to also add esnext

"lib": ["es2018","dom","esnext"]

I believe you have strictNullCheck: true

An empty array with no contextual type ([] in [].concat(arg)) is inferred as never[] under strictNullChecks. and never is not assignable from any other type.

([] as any[]).concat(foo); should do the trick

For much more deeply nested array of arrays such as:[1, 2, 3, [4, [5, [6, [7]]]]]

type NestedArray<T> = Array<NestedArray<T> | T>;const flatten = <T>(input: NestedArray<T>, acc: T[] = []): T[] => {return input.reduce((_: T[], current) => {if (Array.isArray(current)) return flatten(current, acc); acc.push(current);return acc;}, []);};

Usage:

console.log(flatten([1, 2, 3, [4, [5, [6, [7]]]]]));

The answer from Steven worked for my jest tests but not in my actual angular 15 app due to a constraint error I can't figure out. Slightly modified as a regular recursive function to keep Typescript quiet :

import { Injectable} from '@angular/core';export type NestedArray<T> = Array<NestedArray<T> | T>;export class ArrayHelper {public static flatten = <T>(input: NestedArray<T>, res: T[] = []): T[] => {input.forEach((el: T | NestedArray<T>) => {if (Array.isArray(el)) {ArrayHelper.flatten(el, res);} else {res.push(el);}});return res;};}

Example tests:

describe('flattening', () => {it('pass through', () => {expect(ArrayHelper.flatten(['hi', 'there'])).toHaveLength(2);});it('2 level', () => {expect(ArrayHelper.flatten(['hi', 'there', ['that', 'is', 'all', 'folks']])).toHaveLength(6);});it('as per SO', () => {expect(ArrayHelper.flatten([1, 2, 3, [4, [5, [6, [7]]]]])).toHaveLength(7);});});