0

I'm not able to access the methods of the class after typecasting json response from the api to the typescript class.

class Stock {
    name: String;
    purchaseDate: Date;
    constructor() {}
    convertDates() {
        this.purchaseDate = new Date(this.purchaseDate);
    }
}
getAll() {
    return this.http.get(URL + '/find').map(
        (response) => {
            this.stocks = response as Array<Stock>;
            _.forEach(this.stocks, (stock) => {
                stock.convertDates();
            }
        },
        (error) => {
            this.stocks = [];
        }
    );
}

I'm getting an error message as follows: "stock.convertDates is not a function". This works without any error, if I loop through the list of all stocks in the response and create an instance for every stock before calling "convertDates" method. Here is the code for it:

_.forEach(response, (stock) => {
    let newstock = new Stock();
    _.merge(newstock, stock);
    newstock.convertDates();
    this.stocks.push(newstock);
});
whysai
  • 93
  • 1
  • 10
  • Check the accepted answer on this question, it explains what you're running into. https://stackoverflow.com/questions/22875636/how-do-i-cast-a-json-object-to-a-typescript-class – tomaski Sep 16 '17 at 02:25

1 Answers1

3

TypeScript doesn't have runtime casting. It has compile-time type assertions. Confusion between runtime casting and compile-time type assertion seems to be quite common; you're in good company.

Anyway, you used a type assertion when you wrote

response as Array<Stock>;

Type assertions are when you are telling the TypeScript compiler that you know more than it does about what the type of object will be at runtime. Above, you've told the compiler that response will be an array of Stock instances. But you've lied to the compiler, because response is (I'm assuming) actually an array of object literals that do not contain a convertDates() function property. And so at runtime you get the error stock.convertDates is not a function.

TypeScript doesn't really do anything at runtime. If you want an array of instances of the Stock class, you'll need to construct each instance, as you did in the forEach() block. If you do this, your type assertion is no longer a lie, and you won't get a runtime error.


In general you want to use type assertions as sparingly as possible; only use them to silence a TypeScript compiler warning that you are 100% sure will not be an issue at runtime. Even in these cases, it's usually preferable to refactor your code to avoid needing an assertion. For example:

interface Person { name: string; age: string }

//need to assert below because {} is not a Person 
const person: Person = {} as Person;

//populate fields so your assertion is not a lie
person.name = 'Stephen King';
person.age = 69

can be rewritten without assertions as:

interface Person { name: string; age: string }

//no need to assert; TypeScript believes the declaration 
const person: Person = {
  name: 'Stephen King',
  age: 69
} 

Hope that makes sense to you. Good luck!

jcalz
  • 264,269
  • 27
  • 359
  • 360