1

I am handling Http result in a reducer function of an Observable. While a type of the parameter jwt is set as { id: string, auth_token: string, expires_in }, the jwt argument turns out to be a string. I thought TypeScript does parsing automatically. Do I have to do JSON.parse(JSON.stringify(jwt)) by myself?

.mergeMap((jwt: { id: string, auth_token: string, expires_in }) => {})
vter
  • 1,881
  • 1
  • 20
  • 38

2 Answers2

1

If the jwt(JSON) object is retrieved with Http from HttpModule @angular/http you have to parse it to JSON e.g.:

import { Http } from '@angular/http';
.... 
constructor(
   private http: Http
   ...
) {}

this.http.get(url)
  .map((res: any) => {
    return res.json();
  }).subscribe( (jwt: any) => {
       //you got jwt in JSON format
  });

If you use HttpClient from HttpClientModule @angular/common/http (Angular > 4.3.x) you do not need to parse the received data because it is already done.

import { HttpClient } from '@angular/common/http';
....
constructor(
   private http: HttpClient
   ...
) {}

this.http.get<any>(url)
  .subscribe((jwt: any) => {
     //you got jwt in JSON format
  })

More info in this answer

ambussh
  • 740
  • 1
  • 7
  • 18
1

Type checking external code

There is no relationship between the TypeScript source code and the JavaScript outputs that gets executed at run-time. TypeScript is only effective at catching compile-time errors if the compiled types match the run-time types.

Normally, this isn't a problem. But in scenarios where you call out to external code (i.e. AJAX call to fetch data from the server), there is no guarantee that the response will be the type you expect. So you must be cautious in these scenarios.

Your specific example

I suspect that your code has a variable jwt with type any and you just assigned the type to { id: string, auth_token: string, expires_in } when in fact, jwt was of type string as far as javascript is concerned.

In this case, you already found your solution, JSON.parse(str). This converts the json string into a javascript object.

Now that you have an object, you can use duck typing to infer the run-time type and let typescript know about the type at compile-time via type guards.

Solution

function isDate(obj: any): obj is Date {
    return typeof obj === 'object' && 'toISOString' in obj;
}

function isString(obj: any): obj is string {
    return typeof obj === 'string';
}

interface JWT {
    id: string;
    auth_token: string;
    expires_in: Date; 
}

function isJwt(obj: any): obj is JWT {
    const o = obj as JWT;
    return o !== null
        && typeof o === 'object'
        && isString(o.id)
        && isString(o.auth_token)
        && isDate(o.expires_in);
}

function print(jwt: any) {
    if (typeof jwt === 'string') {
        try {
            jwt = JSON.parse(jwt);
        } catch (e) {
            console.error(`String is not JSON: ${jwt}`);
        }
    }
    if (isJwt(jwt)) {
        console.log(`Found jwt: ${jwt.id} ${jwt.auth_token} ${jwt.expires_in}`);
    } else {
        console.error(`Object is not of type jwt: ${jwt}`);
    }
}

print(42);
print('failing');
print(null);
print(undefined);
print({});
print({ id: 'id01', auth_token: 'token01', expires_in: new Date(2018, 11, 25) });

Playground

Try running that code on the TS Playground to see how it inspects the object at run-time.

styfle
  • 22,361
  • 27
  • 86
  • 128