DEV Community

Ruiwen Zeng
Ruiwen Zeng

Posted on

Beyond Array and Map: What `data-structure-typed` Brings to TypeScript Collections

When TypeScript developers need a collection, we almost always start with Array, Map, or Set.

That works for a lot of code.

But once your workload becomes queue-heavy, continuously ordered, range-oriented, or built around real domain objects, those built-in structures start to feel stretched. That is the gap data-structure-typed is designed to fill.

This library is not just “a bunch of data structures for TypeScript.”

What makes it interesting is that it tries to bring a more complete collections world into TS/JS, while still keeping the API and usage style close to the way JavaScript developers already think.

If you want to browse the project first, here are the most useful entry points:

In other words, it is not only about having trees, heaps, deques, and tries.

It is about making them feel usable in everyday TypeScript.


1. A Uniform API Matters More Than People Think

One of the biggest problems with many data structure libraries is not missing functionality.

It is API fragmentation.

Every structure has its own style. Every structure wants you to learn a different mental model. That makes the whole library feel like a bag of unrelated utilities instead of a coherent collections toolkit.

data-structure-typed goes in the opposite direction. It tries to keep familiar JavaScript collection operations available across structures, so developers do not have to mentally switch gears every time they move from one collection to another. The project's Reference and Architecture pages make this design direction especially clear.

import { Deque, DoublyLinkedList } from 'data-structure-typed';

const deque = new Deque([1, 2]);
deque.push(3);
deque.unshift(0);
deque.pop();
deque.shift();

const list = new DoublyLinkedList([1, 2]);
list.push(3);
list.unshift(0);
list.pop();
list.shift();
Enter fullscreen mode Exit fullscreen mode

That may look simple, but it is an important design choice.

The point is not that every structure is identical.

The point is that advanced collections should not feel like an entirely different language.

You can also try this style directly in the official playgrounds:


2. Advanced Collections Should Still Support Expressive Code

A lot of data structure libraries are fine when all you need is add, delete, and has.

But real application code usually wants more than that.

It wants to filter, transform, and aggregate data without dropping back to raw arrays every time.

That is why one of my favorite parts of data-structure-typed is that advanced collections can still participate in expressive, everyday data-processing code. The Reference and Guides are especially good places to explore this style further.

For example, a TreeSet can still be used in a very JavaScript-friendly chain:

import { TreeSet } from 'data-structure-typed';

const users = new TreeSet([
  { id: 1, name: 'Alice', age: 24 },
  { id: 2, name: 'Bob', age: 31 },
  { id: 3, name: 'Charlie', age: 29 },
  { id: 4, name: 'David', age: 22 },
], {
  comparator: (a, b) => a.age - b.age
});

const totalAge = users
  .filter(user => user.age > 26)
  .map(
    user => ({ ...user, senior: user.age >= 30 }),
    { comparator: (a, b) => a.age - b.age }
  )
  .reduce((sum, user) => sum + user.age, 0);

console.log(totalAge); // 60
Enter fullscreen mode Exit fullscreen mode

This is a small example, but it shows something important.

The impressive part is not just that filter, map, and reduce exist.

The impressive part is that they exist on a tree-based collection without forcing you back into arrays first.

That is a very different developer experience from libraries where advanced structures only feel usable at the lowest operational level.


3. Collections Should Plug Into JavaScript, Not Fight It

Another major strength of this library is that the structures do not feel isolated from the language.

If a collection works naturally with JavaScript’s iteration model, it becomes much easier to adopt in real code. You can loop it, spread it, convert it, and pass it around using familiar syntax. This is one of the design ideas called out in the project's Architecture notes and Concepts / theory guide.

import { TreeSet } from 'data-structure-typed';

const tree = new TreeSet([3, 1, 2]);

const arr = [...tree];
const copy = Array.from(tree);

for (const value of tree) {
  console.log(value);
}

const nativeSet = new Set(tree);

console.log(arr);       // [1, 2, 3]
console.log(copy);      // [1, 2, 3]
console.log(nativeSet); // Set { 1, 2, 3 }
Enter fullscreen mode Exit fullscreen mode

This kind of zero-friction interoperability matters a lot.

It means advanced collections are not “special objects you only touch in special ways.”

They behave like natural extensions of JavaScript itself.


4. Stop Using Arrays as Queues

One of the clearest examples in the whole library is Deque.

This is not just a case of “here is another container.”

It exposes a very common mistake in TS/JS codebases: using arrays as queues.

A lot of code ends up looking like this:

const queue = [1, 2, 3, 4, 5];
queue.shift();
queue.push(6);
Enter fullscreen mode Exit fullscreen mode

That works functionally, but it is a poor abstraction for queue-heavy workloads.

A Deque makes the intention clearer and gives you a collection built for front and back operations:

import { Deque } from 'data-structure-typed';

const queue = new Deque([1, 2, 3, 4, 5]);
queue.shift();
queue.push(6);
Enter fullscreen mode Exit fullscreen mode

That is why this example is stronger than it looks.

It does not just say, “Use this because it is faster.”

It says, “A queue is not just an array that happens to call shift().”

That is the kind of conceptual correction good data structure libraries should make.

If you want the exact numbers and benchmark breakdowns, go straight to the official benchmark report and the project’s performance documentation.


5. Some Collections Should Stay Ordered All the Time

Another place where built-in collections start to show their limits is ordered data.

A lot of developers instinctively reach for an array and call sort() whenever they need ranked results. But some datasets are not meant to be re-sorted occasionally. They are meant to stay ordered as updates happen.

That is where a tree-backed ordered collection becomes much more natural.

import { RedBlackTree } from 'data-structure-typed';

const leaderboard = new RedBlackTree([
  [100, 'Alice'],
  [85, 'Bob'],
  [92, 'Charlie']
]);

for (const [score, player] of leaderboard) {
  console.log(`${player}: ${score}`);
}

leaderboard.delete(85);
leaderboard.set(95, 'Bob');

const topPlayers = [...leaderboard.values()].reverse().slice(0, 3);
console.log(topPlayers);
Enter fullscreen mode Exit fullscreen mode

The point of this example is not just that trees can sort.

It is that a leaderboard is not really “an array that I sort from time to time.”

It is a collection whose order should be maintained continuously.

That is a much better fit for workloads like rankings, scoreboards, time-series windows, and other order-sensitive systems. The project’s guides and reference have more examples in this direction.


6. Ordered Collections Need More Than Basic CRUD

What I also like about data-structure-typed is that its ordered collections are not limited to basic membership operations.

Once you move into ordered data, you often want range-friendly, navigable semantics:

  • nearest greater value
  • nearest smaller value
  • boundary lookups
  • range extraction

That is the kind of collection power TS/JS developers often end up rebuilding manually.

A TreeSet makes these operations part of the collection model itself:

import { TreeSet } from 'data-structure-typed';

const scores = new TreeSet([1, 3, 5, 7, 9]);

console.log(scores.ceiling(4)); // 5
console.log(scores.floor(4));   // 3
console.log(scores.higher(5));  // 7
console.log(scores.lower(5));   // 3

console.log(scores.rangeSearch([3, 7])); // [3, 5, 7]
Enter fullscreen mode Exit fullscreen mode

This is where the library starts to feel less like “extra containers” and more like a real collections toolkit.

Because once a collection is ordered, those ordered semantics should be first-class. If that part interests you, the API reference is the best next stop.


7. Real Domain Objects Should Be Able to Live Directly Inside the Collection

Another thing I think this library does especially well is handling real business objects, not just primitives.

A lot of demos in data structure libraries stop at number, string, or simple key-value pairs. That is fine for teaching basics, but real applications usually work with richer domain objects: players, products, users, tasks, transactions, events.

data-structure-typed gives you ways to map those objects into collection structure directly, instead of forcing you to flatten everything ahead of time.

For example:

import { RedBlackTree } from 'data-structure-typed';

type Player = {
  id: number;
  name: string;
  score: number;
};

const players: Player[] = [
  { id: 1, name: 'Alice', score: 100 },
  { id: 2, name: 'Bob', score: 85 },
  { id: 3, name: 'Charlie', score: 92 }
];

const leaderboard = new RedBlackTree<number, Player, Player>(players, {
  isMapMode: false,
  toEntryFn: (player) => [player.score, player]
});

for (const player of leaderboard.values()) {
  console.log(player.name, player.score);
}
Enter fullscreen mode Exit fullscreen mode

Or with TreeSet, where domain objects can be transformed into ordered set elements:

import { TreeSet } from 'data-structure-typed';

interface User {
  id: number;
  name: string;
}

const users: User[] = [
  { id: 3, name: 'Charlie' },
  { id: 1, name: 'Alice' },
  { id: 2, name: 'Bob' }
];

const userIds = new TreeSet<number, User>(users, {
  toElementFn: user => user.id
});

console.log([...userIds]); // [1, 2, 3]
Enter fullscreen mode Exit fullscreen mode

This matters because it makes the library feel much more usable in actual applications.

It is not just “collections for demos.”

It is collections that can sit closer to real domain models.

You can dig deeper into these patterns in the real-world guides and reference documentation.


What Makes This Interesting to Me

What stands out to me about data-structure-typed is not just that it has many structures.

It is that it combines several things that are rarely strong at the same time:

  • a more complete collections model for TypeScript
  • APIs that still feel familiar to JavaScript developers
  • expressive collection-level operations
  • natural interoperability with the language
  • ordered and navigable collection semantics
  • support for real domain objects, not just primitives

That combination is what gives the library its identity.

It is not just trying to bring classic data structures into TypeScript.

It is trying to make them feel like they actually belong there.


Final Thoughts

For me, the most interesting part of data-structure-typed is not any single container.

It is the idea that TypeScript developers should not have to choose between:

  • staying inside familiar JavaScript patterns, or
  • reaching for richer collection abstractions

A good collections library should let you have both.

That is what this project is trying to do.

If you want to explore further, here are the best places to continue:

Top comments (0)