11

How do I define a type or interface describing a deeply nested array in TypeScript?

For example, let's say I am writing a function for testing a path against any number of patterns.

function match(path: string, matcher: Matcher): boolean { /* ... */ }

The Matcher type may be any of the following:

  • string
  • RegExp
  • Matcher[] (note the self-reference)

In other words, the compiler should accept the following:

match('src/index.js', 'lib/**/*');
match('src/index.js', /\/node_modules\//);
match('src/index.js', ['src/**/*', /\.js$/]);
match('src/index.js', ['src/**/*', [/\.js$/, ['*.ts']]]);

But the following should produce a compiler error:

match('src/index.js', {'0': 'src/**/*'});               // Compiler Error!!!
match('src/index.js', ['src/**/*', true]);              // Compiler Error!!!
match('src/index.js', ['src/**/*', [/\.js$/, [3.14]]]); // Compiler Error!!!

Is there a way to achieve this in TypeScript?

1 Answer 1

11

Yes, you can do this in TypeScript. The solution is a bit verbose, but it can be done using a combination of generic type aliases and interfaces.

Start with an interface defining a deeply nested array.

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

So far, the compiler will accept the following:

type Matcher = DeepArray<string | RegExp>;

const m1: Matcher = ['src/**/*', /\.js$/];
const m2: Matcher = ['src/**/*', [/\.js$/, ['*.ts']]];

But the question specifies that the function should also accept a single string or RegExp. This will still produce a compiler error.

const m3: Matcher = 'lib/**/*';         // Compiler Error!!!
const m4: Matcher = /\/node_modules\//; // Compiler Error!!!

We can solve this problem with a generic type alias:

type Deep<T> = T | DeepArray<T>;

And now our type works as expected.

type Matcher = Deep<string | RegExp>;

function match(path: string, matcher: Matcher): boolean { /* ... */ }

match('src/index.js', 'lib/**/*');
match('src/index.js', /\/node_modules\//);
match('src/index.js', ['src/**/*', /\.js$/]);
match('src/index.js', ['src/**/*', [/\.js$/, ['*.ts']]]);

match('src/index.js', {'0': 'src/**/*'});                 // Compiler Error!!!
match('src/index.js', ['src/**/*', true]);                // Compiler Error!!!
match('src/index.js', ['src/**/*', [/\.js$/, [3.14]]]);   // Compiler Error!!!
Sign up to request clarification or add additional context in comments.

1 Comment

Works pretty well for me

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.