1

I have a model class like this:

export class Task {

    public name: string;
    public status: string = "todo";

    public completeTask(): void {
        this.status = "done";
    }   
}

And a service that retrieves a task:

export class TaskService {

    constructor(private _http: Http) {}

    public getTask(): Observable<Task> {
        return this._http
            .get("url")
            .map(res => res.json().data as Task)
    }
}

When I then try to call the completeTask function on the task I get the error message:

TypeError: task.completeTask() is not a function

I get the same result when I cast a JSON literal object to a task.

let task: Task = <Task>{ name: "Make some coffee" }
task.completeTask(); // Results in error

Am I doing something wrong? How can I make sure functions are included?

Aetherix
  • 2,150
  • 3
  • 24
  • 44
  • 1
    The issue is that this is not a type _cast_, but a type _assertion_ -- which does not imply runtime support for type-conversion. You are telling the compiler to believe `task` is of type `Task`, but in fact it's just of type `Object` that was parsed from `json`. This problem with JSON is a somewhat common issue in TypeScript, for which I'm actually working on a solution right now (more like, _testing_ it) with decorators. – John Weisz Apr 29 '16 at 15:51

1 Answers1

3

In fact when you cast, you don't really have an instance of the type you cast. You only declare an object with the literal form not an instance of task.

If it's just a way provided by TypeScript to check that the casted object follows the structure of the type. But it's not something at runtime only at design / compile time.

If you want to be able to use methods of the type you need to instantiate a new object based on the content:

public getTask(): Observable<Task> {
    return this._http
        .get("url")
        .map(res => {
          let content = res.json().data;
          let task = new Task();
          task.name = content.name;
          task.status = content.status;
          return task;
        });
}

See this question for more details:

Edit

Following the @ssube's comment you could declare the Task class like this:

export class Task {
  (...)

  constructor(obj:{name:string, status:string}) {
    this.name = obj.name;
    this.status = obj.status;
  }

  (...)
}

This way the instantiate would be easier:

public getTask(): Observable<Task> {
    return this._http
        .get("url")
        .map(res => new Task(res.json().data));
}
Community
  • 1
  • 1
Thierry Templier
  • 198,364
  • 44
  • 396
  • 360