Categories
TypeScript

Using TypeScript — Generic Collections and Index Types

Spread the love

TypeScript is a natural extension of JavaScript that’s used in many projects in place of JavaScript.

However, not everyone knows how it actually works.

In this article, we’ll look at how to extend generic collections and index types in TypeScript.

Using Generic Collections

TypeScript provides generic versions of various JavaScript collections.

They take type parameters to restrict the types of data that can be populated with them.

Map<K, V> is a JavaScript map with keys restricted to type K and values restricted to type V .

ReadonlyMap<K, V> a map that can’t be modified.

Set<T> is a set whose value type is T .

ReadonlySet<T> is a set that can’t be modified.

For instance, we can define a map by writing:

const map: Map<string, number> = new Map([["foo", 1], ["bar", 2]]);

We pass in strings and values and will get errors from the TypeScript compiler if we don’t.

Generic Iterators

TypeScript also provides us with generic versions of iterators.

Iterator<T> is an interface that describes an iterator whose next method returns IteratorResult<T> objects.

IteratorResult<T> describes a result produced by an iterator with done and value properties.

Iterable<T> defines an object that has a Symbol.iterator property and supports iteration,

IterableIterator<T> an interface that combines Iterator<T> and Iterable<T> interfaces to describe an object with the Symbol.iterator property and has the next and result property.

For instance, we can write:

function* gen() {
  yield 1;
  yield 2;
}

const iterator: Iterator<number> = gen();

We restrict the iterator to only return numbers sequentially.

Also, we can create an iterable object as follows:

const obj = {
  *[Symbol.iterator]() {
    yield 1;
    yield 2;
  }
};

const iteratable: Iterable<number> = obj;

Then we can use it with the spread operator or the for-of loop like any other iterable object.

Creating an Iterable Class

We can create an iterable class as we did with iterable objects.

It has the Symbol.iterator method like the iterable object that we saw before.

For instance, we can write:

class GenericIterable<T> implements Iterable<T> {
  items: T[] = [];
  constructor(...items: T[]) {
    this.items = items;
  }

  *[Symbol.iterator]() {
    for (const i of this.items) {
      yield i;
    }
  }
}

We implemented the Iterable<T> interface with our own iterable class.

As long as it has the Symbo.iterator method and it’s a generator function, then it implemented the interface correctly.

Then we can use it by writing:

const itr: GenericIterable<number> = new GenericIterable<number>(1, 2, 3);

Index Types

TypeScript have index types, which we can use to restrict the values to the keys of another object.

For instance, we can write:

function getProp<T, K extends keyof T>(item: T, keyname: K) {
  console.log(item[keyname]);
}

Then K is restricted to be the key of whatever has the T type with the keyof keyword.

Then we can use it by writing:

interface Person {
  name: string;
}

const person: Person = { name: "joe" };
getProp(person, "name");

We have the Person interface with the name property, and then we can call getProp with the object created with the Person interface and the string 'name' .

If we replace the string in the 2nd argument with anything that’s not in the interface, we’ll get an error.

We can add the type parameters.

For instance, we can write:

getProp<Person, "name">(person, "name");

That works the same as we did without it.

However, we can be more restrictive with the types.

Indexed Access Operator

We can use the keyof operator in an index.

Then we can get the types of all properties and put it into one type alias as a union.

For instance, if we have the interface Person:

interface Person {
  name: string;
  age: number;
}

Then if we write:

type allTypes = Person[keyof Person];

Then allTypes is string | number .

Then we can write:

const foo: allTypes = 1;
const bar: allTypes = "foo";

As we can see, we can assign a string or a number.

Conclusion

Generic collection types are built into TypeScript.

There are types for iterables, iterators, maps, sets, and more.

There are also types for getting keys of members of other types.

By John Au-Yeung

Web developer specializing in React, Vue, and front end development.

Leave a Reply

Your email address will not be published. Required fields are marked *