Utilizing TypeScript Interfaces for Recursive JSON Structures

I am currently working on adapting a product's ontology with its properties using JSON. The structure I have in mind is outlined below.

Each Product (Concept) has two types of properties: 1. Data Properties 2. Object Properties

When using Protege, the typical definitions for these properties are as followsSO Thread:

In Protégé, there are different tabs for creating Object Properties and Datatype Properties. If a property relates individuals to individuals, it needs to be an object property; if it relates individuals to literals, it needs to be a datatype property.

For each property, I believe it should have the following attributes:

name: string
url: string
type: dataprop or objprop
objPropSource: available only for Objproperties

I have designed a small recursive JSON structure as shown below:

{
  "name": "chair",
  "url": "http://namespace.org#chair",
  "type": "main",
  "properties": [
    {
      "name": "height",
      "url": "http://namespace.org#height",
      "type": "dataprop"
    },
    {
      "name": "width",
      "url": "http://namespace.org#width",
      "type": "dataprop"
    },
    {
      "name": "horizontalsurface",
      "url": "http://namespace.org#horizontalsurface",
      "type": "objprop",
      "objPropSource": "http://namespace.org#hasHorizontalSurface",
      "properties": [
        {
          "name": "Legislation",
          "url": "http://namespace.org#legislation",
          "type": "objprop",
          "objPropSource": "http://namespace.org#compliesWithLegislation",
          "properties": [
            {
              "name": "hasLegislationName",
              "url": "http://namespace.org#hasLegislationName",
              "type": "dataprop"
            }
            ]
        }
        ]
    },
    {
      "name": "legislation",
      "url": "http://namespace.org#legislation",
      "type": "objprop",
      "objPropSource": "http://namespace.org#compliesWithLegistion",
      "properties": [
        {
          "name": "hasLegislationName",
          "url": "http://namespace.org#hasLegislationName",
          "type": "dataprop"
        }
        ]
    }
  ]
}

This structure essentially provides a Binary Tree for a chair, where properties like height, width are considered as dataproperties, while horizontalsurface and legislation are treated as objectproperties.

JSON to Interface in Typescript

To visualize how the JSON converts to Typescript Interfaces, I utilized the JSON to TS Online Converter and obtained the following outcome:

interface RootObject {
  name: string;
  url: string;
  type: string;
  properties: Property3[];
}

interface Property3 {
  name: string;
  url: string;
  type: string;
  objPropSource?: string;
  properties?: Property2[];
}

interface Property2 {
  name: string;
  url: string;
  type: string;
  objPropSource?: string;
  properties?: Property[];
}

interface Property {
  name: string;
  url: string;
  type: string;
}

Inference

From my inference, utilizing Interfaces for large recursive JSON structures may not be scalable. As demonstrated in the example above, when dealing with thousands of properties within properties, constantly creating interfaces can become cumbersome.

Expectation

Should I continue using Typescript Interfaces with such a JSON structure, or would it be more efficient to create a Class and implement a conventional method of constructing a Binary Tree as shown below:

export class leaf {
  name: string;
  url: string;
  type: string;
  children: leaf[] = [];
}

Followed by writing a recursive function for parsing through the complete structure?

TL;DR

Can Typescript interfaces effectively handle Large Recursive JSON Structures?

Answer №1

Representing that structure as a recursive interface is straightforward:

interface Property {
  name: string;
  url: string;
  type: string;
  objPropSource?: string;
  properties?: Property[];
}

It seems like the JSON to TS converter you attempted to utilize lacks the capability to identify the recursive nature of your structure.

Here's a functional example:

interface Property {
  name: string;
  url: string;
  type: string;
  objPropSource?: string; // optional property
  properties?: Property[];
};

var p: Property = JSON.parse(getJson());

alert(p.properties[2].properties[0].name);
alert(p.properties[3].objPropSource);

function getJson() {
  return `{
  "name": "chair",
  "url": "http://namespace.org#chair",
  "type": "main",
  "properties": [
    {
      "name": "height",
      "url": "http://namespace.org#height",
      "type": "dataprop"
    },
    {
      "name": "width",
      "url": "http://namespace.org#width",
      "type": "dataprop"
    },
    {
      "name": "horizontalsurface",
      "url": "http://namespace.org#horizontalsurface",
      "type": "objprop",
      "objPropSource": "http://namespace.org#hasHorizontalSurface",
      "properties": [
        {
          "name": "Legislation",
          "url": "http://namespace.org#legislation",
          "type": "objprop",
          "objPropSource": "http://namespace.org#compliesWithLegislation",
          "properties": [
            {
              "name": "hasLegislationName",
              "url": "http://namespace.org#hasLegislationName",
              "type": "dataprop"
            }
            ]
        }
        ]
    },
    {
      "name": "legislation",
      "url": "http://namespace.org#legislation",
      "type": "objprop",
      "objPropSource": "http://namespace.org#compliesWithLegistion",
      "properties": [
        {
          "name": "hasLegislationName",
          "url": "http://namespace.org#hasLegislationName",
          "type": "dataprop"
        }
        ]
    }
  ]
}`;
}

Answer №2

Here are two potential helpers to simplify things for you in the future.

type CreateRecursiveKeys<Keys extends string, T> = {
    [K in Keys]: T & CreateRecursiveKeys<K, T>
} & T


type CreateRecursiveObjectKeys<TKeys extends string, T> = {
    [K in keyof T]: K extends TKeys ? T[K] & CreateRecursiveKeys<K, T[K]>: T[K]
}

Discriminator Types:

type BaseTypes = 'dataprop' | 'objprop'

interface BaseType<TType extends BaseTypes = 'dataprop'> {
    name: string;
    url: string;
    type: TType; 
    properties?: DiscriminatorType   
}

interface ObjProp extends BaseType<'objprop'>{

    objPropSource: string;
}

type DiscriminatorType = BaseType | ObjProp

const root: DiscriminatorType = {
    name:'name',
    type: 'dataprop',
    url:'string',
    properties: {
        name:'name2',
        type:'objprop',
        objPropSource:'sdf',
        url:'str',        
    }
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Utilize the jq tool to transform text data from a .txt file into JSON format

I have extracted values from a txt file using a recursive command: gsutil ls -r gs://bucket-test/** | while IFS= read -r key; do gsutil stat $key; done. The data looks like this: ... The task at hand is to convert this output into JSON format, with each g ...

Issues with property methods not being invoked during JSON deserialization

Within the realm of my coding project, there exists a JSON class file that encompasses three distinct classes. Each class is structured in a similar manner: public class ManifestJSON : INotifyPropertyChanged { [JsonProperty("dataType")] private st ...

In TypeScript and React, what is the appropriate type to retrieve the ID of a div when clicked?

I am facing an issue in finding the appropriate type for the onClick event that will help me retrieve the id of the clicked div. const getColor = (event: React.MouseEvent<HTMLDivElement, MouseEvent>) => { const color = event.target.id; // ...

The search for d.ts declaration files in the project by 'typeRoots' fails

// Within tsconfig.json under "compilerOptions" "typeRoots": ["./@types", "./node_modules/@types"], // Define custom types for Express Request in {projectRoot}/@types/express/index.d.ts declare global { namespace Express { interface Request { ...

Creating an Object Factory that preserves the type: A step-by-step guide

I developed a unique object factory to create instances of all my interfaces: interface SomeInterface { get(): string; } class Implementation implements SomeInterface { constructor() {} get() { return "Hey :D"; } } type Injectable = ...

Using JS or jQuery to redirect to a URL after receiving a JSON object from an event listener

My main aim is to create a process where I can redirect to a different URL based on an interaction in a cross-domain iframe. The desired process looks like this: A user visits example.com/apps/ and completes a form inside a cross-domain iframe. After su ...

Exploration of Binary Search Trees in Python for Testdome

I am seeking a solution to the problem presented on test-dome. Here is the scenario: In a binary search tree (BST), each node's value must be greater or equal to all nodes in its left subtree and smaller than all nodes in its right subtree. Your ta ...

Discussing recursive types A <--> B with generics in TypeScript

I'm currently working with the following TypeScript code: type DormNodePredicate = DormEdge | (() => DormNode<DormNodePredicateRecord>); interface DormNodePredicateRecord { [key: string]: DormNodePredicate; } export class DormNode<DNPR ...

Encountering an issue while attempting to input a URL into the Iframe Src in Angular 2

When I click to dynamically add a URL into an iframe src, I encounter the following error message: Error: Uncaught (in promise): Error: Cannot match any routes. URL Segment: 'SafeValue%20must%20use%20%5Bproperty%5D' To ensure the safety of the ...

Is there an issue with updating state in React using context?

I am looking to utilize context and usehook within the UploadButton component so that I can access the state in the UserButton component. What I am attempting to achieve? When the button in UploadButton is clicked, the state isDialogOpen is set to true. I ...

Angular - Electron interface fails to reflect updated model changes

Whenever I click on a field that allows me to choose a folder from an electron dialog, the dialog opens up and I am able to select the desired folder. However, after clicking okay, even though the folder path is saved in my model, it does not immediately s ...

Populate the table with JSON content using jQuery

I am attempting to populate a table with JSON data using jQuery, but the content within the div remains empty. I need assistance in identifying the error. The array list contains the data (I verified this using console.log(list)). Additionally, list[' ...

JsonConverting, JObjecting, JPropertying

I have a code snippet here that generates NewtonSoft Jobjects JObject o = new JObject(new JProperty("DataSources", new JArray(from p in repDatasource.DataSources select JObject( JProperty("Columns", new JArray (from q in p.columns select new JObject(new J ...

The issue of json_encode not functioning properly when a HTML string is used as a value

I have been troubleshooting this ajax issue for a while now. In my jQuery file, I have the following code: $("#typeForm").ajaxForm({ success : function(html){ alert(html); }).submit(); This code is supposed to call service.php, where the foll ...

Proceed the flow of event propagation using the react-aria button element

In the react-aria library, event bubbling for buttons is disabled. I am facing an issue where my button, which is nested inside a div that acts as a file uploader, does not trigger the file explorer when clicked due to event bubbling being disabled. How ...

Building a list of dictionaries in Python by iterating through a data source

Currently, I am extracting data from an Excel file and aiming to convert it into a JSON format. To achieve this, I am looping through the rows and creating a dictionary for each row. These dictionaries are then added to a list, where the entire list will r ...

Creating an Angular 2 component that utilizes an interface within the constructor

If you have an interface named IData, and you want to develop an Angular 2 component that can accept any Class utilizing IData in its constructor, could this concept be implemented or is it off track? Your insights are greatly appreciated. ...

Dealing with Errors in a POST Request using Angular

I have been following the instructions provided in the official Angular documentation found at https://angular.io/guide/http. I encountered an issue with the handleError function within the post request, as it requires 2 arguments and is displaying an err ...

Enhanced Autocomplete Feature with Select All Option in MUI

Currently, I am utilizing Material UI (5) and the Autocomplete component with the option for multiselect enabled. In addition, I am implementing the "checkbox" customization as per the MUI documentation. To enhance this further, I am attempting to incorpor ...

What is the syntax for implementing this function in TypeScript?

When developing applications in react and typescript, I find myself frequently creating helper functions. However, there are two key points that always give me pause. One of my functions is provided below, showcasing the common dilemmas I face. What shoul ...