45

I'm learning Angular2 and Typescript. I'm working through the Heroes tutorial on angular.io, but applying it to a project I'm converting from ASP.Net. I've run into a problem which I think is due to my lack of understanding, though as far as I can see it matches what the relevant part of the tutorial is doing.

import { Injectable } from '@angular/core';
import {RiskListSummary} from '../Models/RiskListSummary';
import { Observable } from 'rxjs/Rx';
import { Http, Response } from '@angular/http';

@Injectable()
export class RiskAssessmentListService {

    constructor(private http : Http) {}

    private serviceUrl = "http://myserviceurl/";

    getRisks(): Observable<RiskListSummary[]> {
        return this.http.get(this.serviceUrl)
            .map(this.extractData())
            .catch(this.handleError());
    }

    private extractData(res: Response) {
       if (res.status < 200 || res.status >= 300) {
             throw new Error('Bad response status: ' + res.status);
           }
       let body = res.json();
       return body.data || { };
    }

    private handleError (error: any) {
        let errMsg = error.message || 'Server error';
        console.error(errMsg); // log to console instead
        return Observable.throw(errMsg);
    }
}

I'm getting the following error on the line "return this.http.get(this.serviceUrl)":

Error:(20, 16) TS2322: Type 'Observable<{}>' is not assignable to type 'Observable'. Type '{}' is not assignable to type 'RiskListSummary[]'. Property 'length' is missing in type '{}'.

If it makes a difference, I'm using webstorm (latest version), but I think this error is coming straight from the typescript compiler. I was thinking maybe I need a typings file for rxjs, but the tutorial doesn't use one, and none of the ones that I found with a "typings search" made a difference

The following are my dependencies from package.json:

  "dependencies": {
    "@angular/common":  "2.0.0-rc.1",
    "@angular/compiler":  "2.0.0-rc.1",
    "@angular/core":  "2.0.0-rc.1",
    "@angular/http":  "2.0.0-rc.1",
    "@angular/platform-browser":  "2.0.0-rc.1",
    "@angular/platform-browser-dynamic":  "2.0.0-rc.1",
    "@angular/router":  "2.0.0-rc.1",
    "@angular/router-deprecated":  "2.0.0-rc.1",
    "@angular/upgrade":  "2.0.0-rc.1",

    "systemjs": "0.19.27",
    "es6-shim": "^0.35.0",
    "reflect-metadata": "^0.1.3",
    "rxjs": "5.0.0-beta.6",
    "zone.js": "^0.6.12"
Kevin O'Donovan
  • 1,620
  • 1
  • 13
  • 24

5 Answers5

33

I think that your problem is located here:

getRisks(): Observable<RiskListSummary[]> {
  return this.http.get(this.serviceUrl)
     .map(this.extractData()) <== passing result of function
     .catch(this.handleError()); <== passing result of function
}

You could use just passing function reference:

getRisks(): Observable<RiskListSummary[]> {
  return this.http.get(this.serviceUrl)
     .map(this.extractData)
     .catch(this.handleError);
}

but this way you will lose this.

Or you could use bind method to retain this:

getRisks(): Observable<RiskListSummary[]> {
  return this.http.get(this.serviceUrl)
    .map(this.extractData.bind(this))
    .catch(this.handleError.bind(this));
}

however you will lose type checking.

I would leverage arrow functions to be able to use lexical this:

getRisks(): Observable<RiskListSummary[]> {
  return this.http.get(this.serviceUrl)
    .map(res => this.extractData(res))
    .catch(err => this.handleError(err));
}

Without it, the this variable will point to the function where the call is made, instead of the instance that contains getRisks().

qaisjp
  • 722
  • 8
  • 31
yurzui
  • 205,937
  • 32
  • 433
  • 399
  • Thank you, that did the trick. I'd assumed there was something like .Net's extension methods at work in the original example (https://angular.io/docs/ts/latest/guide/server-communication.html#). I think I'll need to start a new project following their example precisely to see if that still gives the same error – Kevin O'Donovan May 14 '16 at 08:00
7

I had same problem, however no matter how I changed as above mentioned,It still not working. Until my college found this map, catch defination as VSC prompted

so the proper change should be like (add type cast the following place)

getRisks(): Observable<RiskListSummary[]> {
  return this.http.get(this.serviceUrl)
    .map<RiskListSummary[]>(this.extractData)
    .catch<RiskListSummary[]>(this.handleError);
}

I am new to TypeScript. so this kind of [method signature] really give me a hammer on the head :(

But finally it worked out :)

Fei
  • 81
  • 1
  • 2
1

According to the getRisks() fn, you are types to return Observable<RiskListSummary[]>. However in your code, you are returning body.data || {}. I believe you need to interface your RiskListSummary[] to your body.data.

Sean Larkin
  • 6,290
  • 1
  • 28
  • 43
  • Yes, that also works (I changed the signature to private extractData(res: Response) : RiskListSummary[]). As a new javascript developer, coming from C#, it makes more sense to me than the lambda function as well. I'm not sure enough of the langage yet to say which is the best answer :-( – Kevin O'Donovan May 14 '16 at 08:15
1

I found the next: AccessTokens was missing. So,i made the change and it workted: On your model (in my case Country) edit de file and change from:

export interface CountryInterface {
  "id"?: any;
  "name": string;
  "order"?: number;  
}

export class Country implements CountryInterface {
  "id": any;
  "name": string;
  "order": number;
   ....
}

To:

export interface CountryInterface {
  "id"?: any;
  "name": string;
  "order"?: number;
  accessTokens: any[];
}

export class Country implements CountryInterface {
  "id": any;
  "name": string;
  "order": number;
  accessTokens: any[];
  ...
}
0

I have similar issue while working on my side project, the problem here is the interface in the function that is managing the returning value es extractData() you can give it an explicit interface which then correct the type of value you are managing.

example

private extractData(res: {status: number}) {
   if (res.status < 200 || res.status >= 300) {
         throw new Error('Bad response status: ' + res.status);
       }
   let body = res.json();
   return body.data || { };
}

and then the status type will be set to type on a compilation run time.

Thank you Lamin-jr