Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Recursive array type typescript

Tags:

typescript

Say I have the type type Atom = string | boolean | number. I want to define a type of array like:

NestedArray = Atom | [a_0, a_1, ... , a_n] where each a_i is an Atom, or a NestedArray.

Can this be achieved in Typescript?

like image 411
nigel Avatar asked Aug 02 '18 15:08

nigel


People also ask

Does TypeScript support recursion?

TypeScript does not optimize tail-called recursive functions.

How do you write a recursive function in TypeScript?

Recursion is a process of calling itself. A function that calls itself is called a recursive function. The syntax for recursive function is: function recurse() { // function code recurse(); // function code } recurse();

Is graph a recursive data structure?

In computer programming languages, a recursive data type (also known as a recursively-defined, inductively-defined or inductive data type) is a data type for values that may contain other values of the same type. Data of recursive types are usually viewed as directed graphs.


2 Answers

Type aliases can't reference themselves, so this naïve approach will fail:

type NestedArray = Atom | Array<NestedArray | Atom> //Type alias 'NestedArray' circularly references itself.

Interfaces can however reference themselves:

interface NestedArray extends Array<NestedArray | Atom> {

}

And we can define an extra union at the top level to handle the root case:

type Atom = string | boolean | number

interface NestedArray extends Array<NestedArray | Atom> {

}

type AtomOrArray = Atom | NestedArray;

//Usage
let foo: AtomOrArray = [
    "",
    1, 
    [1, 2, ""]
]   

let bar: AtomOrArray =  ""
like image 147
Titian Cernicova-Dragomir Avatar answered Sep 28 '22 01:09

Titian Cernicova-Dragomir


Now typescript allow type circularly references itself, such like this:

type RArray = (string | RArray)[];

And if you want to define a generic type in recursive function, can use interface ...<T> extends (T|...<T>)[]

interface RA<T> extends Array<T | RA<T>> { }

For example, create a function to get sum recursivily

function sum(arr: RA<number>) {
  let res = 0;
  arr.forEach((n) => {
    if (Array.isArray(n)) {
      res += sum(n);
    } else {
      res += n;
    }
  });
  return res;
}
console.log(sum([1, 2, 3, [4, [5]], [[6]]]))
// output: 21

But this way has some disadvantage, compiler can't know the specific type of n

arr.forEach((n) => {
    if (Array.isArray(n)) { // typeof n: `number | RA<number>`
      res += sum(n);
    } else {
      res += n;
    }
  });
like image 41
syfless Avatar answered Sep 28 '22 00:09

syfless