---
title: 'Angular2: using TypeScript generics'
url: https://blog.octoperf.com/angular2-using-typescript-generics/
date: '2016-04-04'
authors:
- gpereira
categories:
- Development
tags:
- Angular
- Typescript
description: A sample usage for TypeScript generics when using Angular2 Http service.
---

# Angular2: using TypeScript generics

**This post is an example of TypeScript generics usage, when writing an Angular2 application.**  

I'm working on a Stripe administration console. Stripe is a payment / subscription SaaS service that we use at [OctoPerf](https://octoperf.com).

I need to create pages that lists all our customers and plans. But Stripe returns paginated lists, and we need to do successive calls to their API to get the complete data. 
This mechanism is the same for both customers and plans lists. **So we can factorise our code using generics** in this particular case. 

## The generic model
 
The first step is to create a common interface for customers and plans. They share a String id.
```javascript
export interface HasId {
  id:string;
}
```


We will see later that this id is used while making successive calls to Stripe API, thus the interface. 

Then we just need to declare our customer and plan classes:
```javascript
import {HasId} from './HasId';

export class Customer implements HasId{
  constructor(public email: string, public id:string) {
  }
}
```

```javascript
import {HasId} from './HasId';

export class Plan implements HasId{
  constructor(public name: string, public id:string) {
  }
}
```

The real objects have more members, that we ignore for this example.

**Note:**

> We could also have used interfaces for Customer and Plan instead of classes.

Stripe always returns paginated list. So when you ask for data you may specify:

* a _limit_ (from one to 100) of number of elements returned,
* and the _starting_after_ param, which is the ID of the last element of the previous call made to their API.

**We declare a paginated list, that uses a generic array of _HasId_ for its data `<T extends HasId>`**:

```javascript
import {HasId} from './HasId';

export class PaginatedList<T extends HasId> {
  constructor(public has_more: boolean,
              public data:T[]) {
  }
}
```

The _has_more_ parameter is used to indicate when we reached the end of the overall data.

## The generic usage

The _StripeClient_ service lets us retrieve Customers and Plans alike: 

```javascript
import {Injectable} from 'angular2/core';
import {Http, Headers, URLSearchParams} from 'angular2/http';
import {Customer} from './Customer';
import {Plan} from './Plan';
import {PaginatedList} from './PaginatedList';
import {HasId} from './HasId';

const stripeUrl: string = 'https://api.stripe.com/v1';

@Injectable()
export class StripeClient {
  private apiKey: string = '';

  constructor(private http: Http) {
  }

  getApiKey(): string {
    return this.apiKey;
  }

  setApiKey(apiKey: string) {
    this.apiKey = apiKey;
  }

  getCustomers(callback: (customers: PaginatedList<Customer>)=>void) {
    this.getData('/customers', null, callback);
  }

  getPlans(callback: (plans: PaginatedList<Plan>)=>void) {
    this.getData('/plans', null, callback);
  }

  private getData<T extends HasId> (endpoint: string, starting_after: string, callback: (data: PaginatedList<T>)=>void) {
    const params: URLSearchParams = new URLSearchParams();
    params.set('limit', '100');
    if (starting_after){
      params.set('starting_after', starting_after);
    }

    this.http.get(stripeUrl + endpoint, {headers: this.getAuthHeaders(), search: params})
      .map(res => res.json())
      .subscribe((paginatedList: PaginatedList<T>) => {
        callback(paginatedList);
        if (paginatedList.has_more){
          this.getData(endpoint, paginatedList.data[paginatedList.data.length - 1].id, callback);
        }
      });
  }

  private getAuthHeaders(): Headers {
    var headers = new Headers();
    headers.append('Authorization', 'Bearer ' + this.apiKey);
    return headers;
  }
}
```

The `getData` method is the core of this service. It recursively calls Stripe API and gives the results to the [callback parameter](https://blog.octoperf.com/angular2-method-callback-type-in-typescript/).
**It uses the _type variable_ `<T extends HasId>`: T can be replaced by any class or interface that extends HasId.** So T can either be Customer or Plan.

Its `callback` parameter can then also take a paginated list of Plans or Customers. And we use the T type variable when we set the type of the results: `.subscribe((paginatedList: PaginatedList<T>) => {...}`.
   
**Note:**

> It's a bit more cumbersome to set headers and query parameters in Angular2 than in AngularJS: you have to create specific objects:
> 
> * `new Headers();` in the `getAuthHeaders()` method so set the authentication headers,
> * `new URLSearchParams();` to set the _limit_ and _starting_after_ query parameters.
   
The `getPlans` and `getCustomers` methods hide this complexity to the callers: they use non generic callback and are simply used like this:
```javascript
@Component({
  ...
})
export class Customers {
  private customers: Customer[];
  private loading: boolean;

  constructor(private client: StripeClient) {
    this.customers = [];
    this.loading = true;
  }

  ngOnInit() {
    console.log('Customers');
    this.client.getCustomers((customers: PaginatedList<Customer>) => {
      this.customers = this.customers.concat(customers.data);
      this.loading = customers.has_more;
    });
  }
}
```

As the callback may be called more than once (if there are more than a hundred results) we need to concat the results.

To know more about [how to unit test this service, take a look at my next article](https://blog.octoperf.com/angular2-hard-time-unit-testing-http-requests/).
