DEV Community

Cover image for How to create a type for complex JSON object in TypeScript?
Ankit Tanna
Ankit Tanna

Posted on

How to create a type for complex JSON object in TypeScript?

Imagine having to deal with a complex JSON object and still having TypeScript typecheck in place. The complex objects cause us to use any in the code in order to navigate around the Typecheck of TypeScript.

Sometimes, we may also have to turn off the linter rules in order to make this work.

// eslint-disable-next-line @typescript-eslint/no-explicit-any
Enter fullscreen mode Exit fullscreen mode
private modifyRecord(record: any) {...}
Enter fullscreen mode Exit fullscreen mode

We can define our custom types like below:

type JSONValue =
    | string
    | number
    | boolean
    | JSONObject
    | JSONArray;

interface JSONObject {
    [x: string]: JSONValue;
}

interface JSONArray extends Array<JSONValue> { }
Enter fullscreen mode Exit fullscreen mode

These kind of type is known as Recursive Type Aliases. After TypeScript 3.7 we can also define it in a confined way:

type JSONValue =
    | string
    | number
    | boolean
    | { [x: string]: JSONValue }
    | Array<JSONValue>;
Enter fullscreen mode Exit fullscreen mode

JSONValue circularly references itself.

Happy coding!

PS: Don't use any in the TypeScript.

Top comments (13)

Collapse
 
loucyx profile image
Lou Cyx • Edited

If you want to have types based on a JSON you know (like an API response), you can use stuff like json2ts, and if you have that JSON in a file, you can just import it and use typeof:

import data from "./data.json";

export type JSONData = typeof data;
Enter fullscreen mode Exit fullscreen mode

If the API has swagger support, there are several tools that generate types from swagger files such as openapi-typescript.

The generic approach in your post doesn't look that useful from my point of view, mainly because you'll have to do a bunch of type checking and type casting inside that function to make it work as expected, so even if you didn't used any, is almost like you did with extra steps.

Collapse
 
csandman profile image
Christopher Sandvik

If you're hitting an external API (without using someting like tRPC) your best bet is to do schema validation at runtime. You can always just assert that the values coming back match a structure you define as an interface, but if the endpoints change, you might not realize until your app starts breaking.

The tool I've seen to do this is zod. It's TS first approach lets you define a schema, and validate your data against it, and the data it returns will either throw an error if the structure is wrong, or return the data already statically typed.

No assuming the data is structured how you expect it, instead checking the structure and applying an interface all in one go!

Collapse
 
loucyx profile image
Lou Cyx

io-ts is great for runtime validation as well, but my suggestion was mainly for when you have a static JSON you know, not a dynamic JSON response you don't. For those ideally you should have as you said an abstraction layer on top that does validations :)

Collapse
 
ankittanna profile image
Ankit Tanna

I liked this portal until it stopped creating relevant interfaces. Only a human mind currently can understand good logical interface.

Collapse
 
lolmaus profile image
Andrey Mikhaylov (lolmaus)

Thanks!

PS You forgot null. 😬

Collapse
 
jonrandy profile image
Jon Randy 🎖️ • Edited

I just don't get it. Just use plain JS and stop creating more work for yourself 😉

Collapse
 
piotrlewandowski profile image
Piotr Lewandowski • Edited

You kinda replied to yourself: you just don't get it. Taking care of the quality of your work is not "creating more work" - it's making sure you don't have to put work to fix your half-arsed code in the future.

Collapse
 
shuckster profile image
Conan

Come now, both of you. Just because you DO type your code doesn't mean you necessarily increase it's quality, and just because you DON'T type your code doesn't mean you're saving yourself from extra work.

Thread Thread
 
piotrlewandowski profile image
Piotr Lewandowski

I don't type all of my code (as in not in every project I work on), but when I have the opportunity to use TS it definitely helps me to catch errors earlier. So yeah, I'd say it does help to increase quality, especially when you work in a team, TS helps to keep consistency on the codebase. Also refactoring: so much easier with TS!
Disclaimer: I still sometimes swear at TS because I'm still learning it :) Just this week I invented at least 5 new swearwords in at least 2 languages I speak ;)

Collapse
 
ankittanna profile image
Ankit Tanna

You don't realise the potential of typescript.

Good luck resolving console error messages and run time errors 😂

Collapse
 
yadimon profile image
Dmitry Gorelenkov

how to access a deep property with this types? like
myJsonObj.subObj1.hisProperty gives me an error in typescript 4.x?

Collapse
 
anentropic profile image
anentropic

JSON also has null

Collapse
 
franzzemen profile image
Franz Zemen

Thanks. Useful for toJSON() implementations.