14

How do I define a type for

string|string[]|string[][]|string[][][] // ad infinitum

in typescript?

edit: the solution would be:

type Rec = string | string[] | Rec[]

but that is not allowed.

Here is my usecase:

interface RecursiveArray<T> {
    [index: number]: (RecursiveArray<T> | T);
}

type Recursive<T> = T|RecursiveArray<T>

function stringValue (value: Recursive<string|boolean>): Recursive<string> {
  if (typeof value === 'boolean') {
    return value ? 'true' : 'false';
  }
  if (Array.isArray (value)) {
    return (value).map (stringValue);
  }
  return stringValue(value);
}
4
  • 1
    Simplest answer is just type any, but that's not well typed. Have you tried to type it as an Array<string>? Commented Jun 23, 2017 at 6:50
  • 1
    Can I ask you for the use case of an infinite deep array? Commented Jun 23, 2017 at 7:06
  • The only solution I can see would make a circularly reference that typescript detect automatically, more information about your use case could help Commented Jun 23, 2017 at 8:17
  • added usecase in question Commented Jun 27, 2017 at 8:25

5 Answers 5

15

Unlike the answer by Rodris you won't need to add any of the built in Array properties like map, length, etc.

type RecursiveArray = Array<RecursiveArray | string>;

let arr: RecursiveArray = [];
arr[1] = [];
arr[1][2] = [];
arr[1][2][2] = "item 122";

// In the other answer this throws a type error
arr[1][2][3] = ['1', '2', '3'];
arr[1][2][3].forEach(item => console.log(item));

More over, you can make it generic to specify the type:

type RecursiveArray<T> = Array<T | RecursiveArray<T>>;
Sign up to request clarification or add additional context in comments.

3 Comments

Nice solution!!
Same error as in my question: Type alias 'RecursiveArray' circularly references itself.
@Galdor For me, updating TypeScript fixed it. It seems this feature was added after v4 or v5. not sure.
3

You can create a recursive interface.

interface RecursiveArray {
    [index: number]: (RecursiveArray | string);
    length: number;
}

let arr: RecursiveArray = [];
arr[1] = [];
arr[1][2] = [];
arr[1][2][2] = "item 122";
arr[1][2][3] = "item 123";

3 Comments

Nice solution, but RecursiveArray does not have a length property
You may add a length property if needed. Did it to the answer.
This requires you to add in each array property, check my answer which avoids this.
1

I just came accross this question and didn't found any type defining solution, so I came up with this type (which I am quite proud of tbh):

type NestedArray<T, Depth extends number = 1> = Depth extends 0 ? T : NestedArray<T[], [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10][Depth]>;

type string2D = NestedArray<string, 2>; //=> string[][]

type string3D = NestedArray<string, 3>; //=> string[][][]

This will only work with up to 10 levels of recursion (look at the [-1, ..., 10] Array, just extend it if you need more). I hope this will help future googlers

Comments

0

Meanwhile in typescript 5 there is no error anymore, so this works:

type RecursiveStringArray = string | string[] | RecursiveStringArray[];

Thanks to Movahhedis comment

Comments

0

My big issue was getting it to work with Array.flat(), I came up with this type that takes a Depth to make sure that I can choose the depth to match Array.flat

type DeepArray<T, Depth extends number = 2> = {
    done: T;
    recur: T[] | DeepArray<T[], [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20][Depth]>;
}[Depth extends 0 ? 'done' : 'recur'];

Example:

function testFlattenDeep(arr: DeepArray<string, 3>): string[] {
    return arr.flat(3)
}

testFlattenDeep(['1', '2', '3']);
testFlattenDeep([
    ['1', '2'],
    ['3', '4'],
    ['5', '6']
]);
testFlattenDeep([
    [
        ['1', '2'],
        ['3', '4']
    ],
    [
        ['5', '6'],
        ['7', '8']
    ]
]);

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.