5

Scenario
I know the shape of the data that is returning from an API call, and I want it to have some extended functionality via a TypeScript class. I already know that I can do the following to fulfill this requirement:

this.http
    .get(url)
    .map((res: Response) => {
        return res.json().map((obj: Type) => {
            return Object.assign(new Type(), obj);
        });
    })
    // And so on  

Would it be possible to make a generic implementation of this similar to the following:

get<T>(url: string): Observable<T> {
    return this.http
        .get(url)
        .map((res: Response) => {
            return res.json().map((obj: T) => {
                return Object.assign(new T(), obj); // not sure how to approach this
            });
        });
}   

The above works for returning a straight up JavaScript Object, but will I'm not sure how to run assign on a generic type.

1 Answer 1

5

I was able to make it work by creating a factory class for new generic types. If anyone else can propose a better alternative, I'll mark it as the answer.

Here's what I did:

factory.ts

export class Factory<T> {
  constructor(private type: new () => T) { }

  getNew(): T {
    return new this.type();
  }
}  

app.service.ts

@Injectable()
export class AppService {
  users = new BehaviorSubject<User[]>([]);

  constructor(public http: Http) { }

  getUsers() {
    const userFactory = new Factory<User>(User);    
    this.http.get('https://jsonplaceholder.typicode.com/users')
      .map((res: Response) => this.extractGeneric<User>(res, userFactory))
      .catch(this.handleError)
      .subscribe(
        data => this.users.next(data),
        err => console.log(err)
      )
  }

  extractGeneric<T>(res: Response, factory: Factory<T>) {
    return res.json().map((obj: T) => {
      return Object.assign(factory.getNew(), obj);
    });
  }  

  handleError(error: Response | any) {
    let errMsg: string;    
    if (error instanceof Response) {
      const body = error.json() || '';
      const err = body.error || body.Message || JSON.stringify(body);
      errMsg = `${err}`;
    } else {
      errMsg = error.message ? error.message : error.toString();
    }    
    console.error(errMsg);
    return Observable.throw(errMsg);
  }
}

See this project on StackBlitz

Sign up to request clarification or add additional context in comments.

1 Comment

You can write new Factory(User);. Anyway, you should somehow reffer class constructor (User for example). The only alternative of dirrect reffering may be using decorator, but it would be just syntax sugar. So, your solution is right I think.

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.