How to go from JSON to proper typed Classes/Objects/Types?

Very generic Typescript question:

I use an API that returns all kind of JSON payloads. Most are just objects, some also arrays/lists of objects. Now I could of course just work with these in my Ionic app, but I assume that would be almost as bad as defining everything as any and then just throw in what I get (I read my share of @rapropos messages here in the forum)… I sure have my problems with giving methods the wrong data because it is a similar Type, but not the same. Having it done right should help here.

What is the best way to get the JSON data I retrieve from the API into proper typed Classes/Objects?

Bonus problem:
The API is not the cleanest one. Many result lists are not a “Array of Foo” but “Array of generic object and a property foo with Foo in it, and some random other properties (of the category action_done_at)”. How to handle this?

(PS: Please excuse my Programming 101 ignorance of Class, Object, Type, … But feel free to correct me.)

I am a big fan of TypeScript’s interfaces for this job. They’re especially well-suited for data that springs into existence outside the scope of the app (i.e. that comes from external sources like HTTP backends), because they are duck-typed and contain no problematic quasi-intelligent logic.

Most of this can be handled by either optional properties or union types.

Thanks as always.

My first question after reading this of course was what the difference between a class and an interface is in Typescript. Just because in other language the existence of actual implementation is the difference, doesn’t mean it has to be the same in TS/JS land:

This seems like a logical first answer:

But what do the docs say?
https://www.typescriptlang.org/docs/handbook/interfaces.html

Interfaces describe the public side of the class, rather than both the public and private side. This prohibits you from using them to check that a class also has particular types for the private side of the class instance.

https://www.typescriptlang.org/docs/handbook/classes.html

Unlike an interface, an abstract class may contain implementation details for its members.

So yeah, it’s really about implementation details being present or not.

(By the way: It’s “funny” how the TS handbook goes from “ok, I get that” to “wtf are they talking about” the more you scroll down these links…)

https://www.typescriptlang.org/docs/handbook/interfaces.html#optional-properties
https://www.typescriptlang.org/docs/handbook/advanced-types.html#union-types

Ok, both understandable and reasonable.

If I understood correctly, interfaces would help to make sure that e.g. a method consuming one of my objects only takes ones with the correct properties. And by that I would make sure, that I don’t accidentally use wrong or incomplete responses from slightly different endpoints. Correct?

What strategy would I use to go from the JSON to the actual objects if I choose to go with classes because “logic” might actually be nice? Is there some clever way to transform these JSON thingies I get into proper objects (or lists of objects)?

What I mean by bashing on “problematic quasi-intelligent logic” in data containers is that I don’t want implementation details there, or even the possibility of them.

Object lifecycle management is an extremely thorny problem in any environment, and Angular’s approach (which I like) is “you, the app developer, should not have anything to do with it”. Hence, DI and garbage collection.

When we define classes that aren’t managed by DI, such as for data containers, we end up instantiating them manually with new, and that is inconsistent with the framework’s paradigm.

This points to why interfaces are a better fit for Angular than abstract classes. Dependency Injection encourages (almost requires) black box data structures. In principle, you know nothing about what’s going on internally with a provider you inject. This is different from Java-style Object Oriented Programming, where both the page and the “provider” know important information about the structure of an object.

Also, to make a short comment on a deep subject, I try to bring as much immutability to Javascript as possible. Interfaces are better for that than classes, because of deep copy issues.

So I independently arrived at the importance of using interfaces to define objects in Angular. I was a bit surprised when I first saw @rapropos also thought they were critical. I felt as though I was really onto something.

Anyway, main point being: read the Typescript docs through the eye of reactive programming / Angular.

1 Like

I like this one here as it gives actual examples and shows the “effects” using a interface/class has on the JS: https://jameshenry.blog/typescript-classes-vs-interfaces/

Also be aware that Angular itself suggest not using Interfaces at all: https://angular.io/guide/styleguide#interfaces

Thanks, problem is probably that I am a nothing programmer (not OOP, not reactive…) and just make stuff “work” somehow :wink:

One thought I have after reading the jameshenry blog post (that I actually already had open in a tab after a Google search earlier):

How does an interface help at all when I define some JSON response to be of this interface, if it is only available at compile time!? The JSON can be whatever format it wants when it is actually returned by the server. If the interface doesn’t exist then, there can be no checking, right? I would have to use a class to make sure the data this one call returns matches my expectation then, wouldn’t I?

Or did I misunderstand?

I think the premise of the first bit of that piece (about tsc not knowing that a Response is a Response unless manually told so) is no longer true, and hasn’t been for some time, at least since generics have been part of TypeScript.

I don’t consider any of those reasons as being applicable for PODs: we aren’t doing class-plus-interface, just interface, and we explicitly do not want them managed by DI; they are born from JSON received from a source external to the application.

At least the way I write things, all interaction with JSON happens in only one place: the service provider that is the first responder to Http calls. Said provider has a bunch of methods like so:

thingyById(id: string): Observable<Thingy> {}
thingiesByFlavor(flavor: string): Observable<Thingy[]> {}

So everywhere outside of this provider, such as in pages and other providers, we get all the benefits of type safety even if Thingy is an interface. Code autocompletion, warnings if we misspell properties, and self-documentation of what a Thingy is supposed to look like.

Now, it’s true that we’re not aggressively checking that the JSON really does fit the Thingy mold, but that wouldn’t magically happen simply by making Thingy a class, either. One would have to write validation code. The only time I think this would be a worthwhile endeavour would be if you don’t truly trust the backend API, but I would think that’s an extremely rare case and not worth the processing overhead in general.

I don’t think that’s the same thing. I use classes as interfaces also – it helps for AOT compiling. But that’s a fancy topic. But I don’t use those classes as models for objects. Instead, they are models for actions that can be taken on objects. The objects are defined with interfaces.

That’s probably an opaque paragraph, sorry. But it’s the best I can do at the moment.