Converting JSON into an interface in TypeScript and verifying its validity

How can I convert a JSON string to a nested interface type and validate it?

Although my model is more complex, here is an example:

export interface User = {
    name: Field;
    surname: Field;
};
export interface Field = { icon: string; text: string; visibility: boolean };
export interface Users = User[]

The conversion could be represented as:

export type user = {
    name: field;
    surname: field;
};
export type field = { icon: string; text: string; visibility: boolean };
export type users = user[]

It could also be implemented using classes.

Here is a sample JSON data:

[
{
"name": { "text": "David", "icon": "icon1.png", "visibility": true },
"surname": { "text": "Smith", "icon": "icon2.png", "visibility": true }
},
{
"name": { "text": "Arthur", "icon": "icon3.png", "visibility": true },
"surname": { "text": "L.", "icon": "icon6.png", "visibility": true }
},
{
"name": { "text": "Anthony", "icon": "icon1.png", "visibility": false },
"surname": { "text": "Isaacson", "icon": "icon2.png", "visibility": true }
},
{
"name": { "text": "Mike", "icon": "icon3.png", "visibility": true },
"surname": { "text": "Jobs", "icon": "icon5.png", "visibility": false }
}
]

Update:

Here is an example illustrating why Chithambara's method may not be valid: Playground

Answer №1

If you find that your validation requirements are quite complex, one option to consider is using a tool like io-ts. This library can automatically generate validations at runtime based on the metadata present in your code.

For simpler needs, you can make use of UserDefined Type Guards.

A type guard essentially takes an unknown (or any, which functionally behaves the same within this context) and informs the compiler that the provided object is compatible with a specific interface.

export interface Field {
  icon: string;
  text: string;
  visibility: boolean;
}

export interface User {
  name: Field;
  surname: Field;
}

function isField(obj: any): obj is Field {
  return (
    obj != null &&
    typeof obj.icon === "string" &&
    typeof obj.text === "string" &&
    typeof obj.visibility === "boolean"
  );
}

function isUser(obj: any): obj is User {
  return obj != null && isField(obj.name) && isField(obj.surname);
}

const userProto: User = {
  name: null,
  surname: null
};

function isUserDynamic(obj: any): obj is User {
  return obj != null && Object.keys(userProto).every(fieldName => isField(obj[fieldName]));
}

function validateUserArray(obj: any): obj is User[] {
  if (obj == null) {
    throw new Error("The array cannot be null");
  }
  if (!Array.isArray(obj)) return false;

  obj.forEach((user, i) => {
    if (!isUser(user))
      throw new Error(
        `Error at index ${i}: ${JSON.stringify(user)} is not a valid user.`
      );
  });

  return true;
}

const json = `[
  {
    "name": { "text": "David", "icon": "icon1.png", "visibility": true },
    "surname": { "text": "Smith", "icon": "icon2.png", "visibility": true }
  },
  {
    "name": { "text": "Arthur", "icon": "icon3.png", "visibility": true },
    "surname": { "text": "L.", "icon": "icon6.png", "visibility": true }
  },
  {
    "name": { "text": "Anthony", "icon": "icon1.png", "visibility": false },
    "surname": { "text": "Isaacson", "icon": "icon2.png", "visibility": true }
  },
  {
    "name": { "text": "Mike", "icon": "icon3.png", "visibility": true },
    "surname": { "text": "Jobs", "icon": "icon5.png", "visibility": false }
  }
]`;

const deserialized: any = JSON.parse(json);

let validatedArray: User[];

if (validateUserArray(deserialized)) {
  validatedArray = deserialized;
}

Answer №2

Looking for a simpler way to maintain your code? Let typia do the work for you.

By using typia, you can generate the function with ease. Take a look at the TypeScript input below:

import typia from "typia";

export interface User {
    name: Field;
    surname: Field;
};
export type Field = { icon: string; text: string; visibility: boolean };

const IsUser = typia.createIs<User>();

const u1 = {
    name: { icon: "i", text: "t", visibility: true },
    surname: { icon: "i", text: "t", visibility: true },
};

const u2 = {
    name: { text: "t", visibility: true },
    surname: { icon: "i", text: "t", visibility: true },
};


if (IsUser(u1)) {
    console.log("u1 is User");
} else {
    console.log("u1 is not User");
}

if (IsUser(u2)) {
    console.log("u2 is User");
} else {
    console.log("u2 is not User");
}

The resulting JavaScript code is shown below:

import typia from "typia";
;
const IsUser = input => {
    return "object" === typeof input && null !== input && ("object" === typeof input.name && null !== input.name && ("string" === typeof input.name.icon && "string" === typeof input.name.text && "boolean" === typeof input.name.visibility) && ("object" === typeof input.surname && null !== input.surname && ("string" === typeof input.surname.icon && "string" === typeof input.surname.text && "boolean" === typeof input.surname.visibility)));
};
const u1 = {
    name: { icon: "i", text: "t", visibility: true },
    surname: { icon: "i", text: "t", visibility: true },
};
const u2 = {
    name: { text: "t", visibility: true },
    surname: { icon: "i", text: "t", visibility: true },
};
if (IsUser(u1)) {
    console.log("u1 is User");
}
else {
    console.log("u1 is not User");
}
if (IsUser(u2)) {
    console.log("u2 is User");
}
else {
    console.log("u2 is not User");
}

The output will be:

u1 is User
u2 is not User

Simplify your code maintenance by utilizing the IsUser function provided by typia.

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

How can TypeScript allow an argument to only accept keys that match another argument?

I'm currently developing a library that deals with linked lists. The current implementation is hardcoded to work with a list node type containing a "next" field that points to the next node of the same type. However, I am looking to make it more flexi ...

When working in React, I often encounter the frustrating TypeError: Cannot read property 'content' of undefined error

Trying to customize a React project, I attempted to add a class to a div using the following code: <div className={styles.content}> In the case of deleting the Data Source, you will lose all configuration sett ...

The usage of the import statement outside a module is not permitted in a serverless Node application

I am currently in the process of migrating a serverless AWS lambda microservices API to TypeScript. My goal is to retain the existing JavaScript files while incorporating more TypeScript files as we progress. However, I am encountering difficulties with co ...

Efficiently extracting data from a JSON dictionary by separating keys and values into individual arrays in Objective C

I'm currently working on accessing currency data from the www.fixer.io API by parsing JSON. I've encountered some challenges trying to extract the keys and values from the "rates" dictionary. It's essential for me to separate them so that I ...

Warning: The gulp_jspm module is now deprecated and will be removed

Whenever I run gulp_jspm, a DeprecationWarning pops up. Is there an alternative method to generate my bundle files without encountering this warning? It seems like when I used gulp-jspm-build, I had to include some node files that were not necessary before ...

Transfer information from the ajax success event to an external else/if statement

I am encountering an issue when trying to transfer the appropriate result from my ajax success function to an external function for further use. Currently, I am using user input to query a data source via ajax and receiving the object successfully. The re ...

Transmit information from a bean to JavaScript using JSON in JavaServer Faces

I need help transferring my arraylist from a managedBean to JavaScript code. The bean code is shown below: public void getDataAsJson(){ String [] dizi={"Tokyo","Jakarta","New York","Seoul", "Manila","Mumbai","Sao Paulo","Mexico City", ...

Guide on associating an array of object keys with an array of their corresponding values within a specified object

const obj = { wheels: 4, lights: 2, doors: 4 } customMapFunction(obj, { properties: ["wheels", "lights"], formatter: (wheels, lights) => `${wheels}-${lights}` // "4-2" }) How do I define the types for customMapFunction in TypeScript to ensure th ...

Is it possible to utilize Typescript and Browserify in tandem?

As I explore the compatibility of TypeScript and Browserify, one perplexing aspect stands out - they both utilize 'require' but for different purposes. TypeScript uses 'require' to import other TS modules, while Browserify employs it to ...

Is Mysqli_query yielding an empty result set due to a possible character encoding issue?

Need help with troubleshooting this PHP code: <?php $data = json_decode($_POST['data'], true); $user = mysqli_real_escape_string($conn, $data['username']); $password = md5($data['password']); $query = mysqli_query($conn, & ...

Having difficulty associating variable values in JSON using JQ mapping function

Is there a way to update the value of an environment variable in a JSON file using another variable as a reference? Here is an example that works: cat taskdef.json | jq ' .taskDefinition.containerDefinitions[].environment | map(if .name == "ARTI ...

What is the best way to verify both a null value and a length simultaneously within a template condition?

There is a data that can be null or an empty array, but the template should still be rendered if leaseApDto is not null or has a length greater than 0. I attempted to use the condition model.leaseApDto !== null || model.leaseApDto.length !=== 0, but they ...

Utilizing Typescript for constructor parameter assignments

Within my codebase, there exists an interface: export interface IFieldValue { name: string; value: string; } This interface is implemented by a class named Person: class Person implements IFieldValue{ name: string; value: string; const ...

Transforming a jsonObject into a JavaScript array

I am working with a JSON object and I need to extract data from it to create an array. Here is an example of the desired array: ['city 1','city 2','city ..'] Below is the JSON output: {"\u0000*\u0000_data":[{"id": ...

What is preventing me from utilizing true/false/null in a Python object that is passed to json.dumps()?

Currently, I am attempting to interpret JSON in order to change true, false, null to True, False, None for Python It is my understanding that I will need to utilize the json library Even after following the guidance provided in this particular thread, ...

Dynamic data manipulation with Angular ReactiveForms

One of the challenges I am facing involves using formArray for my list of products. Specifically, I am trying to access the value of product_code in my .ts file similar to [ngModel] so that I can manipulate the data accordingly. Can anyone provide guidance ...

Having trouble parsing an API response in node JS - It appears to be structured as an array

I've recently started working with Node JS and encountered a problem while trying to retrieve status information on public transport using an API. The response I receive appears to be in the form of an array of objects, but I'm struggling to pars ...

Setting up pagination in Angular Material can sometimes present challenges

After implementing pagination and following the guidelines provided here. This is my code from the app.component.ts file - import { Component, OnInit, ViewChild } from '@angular/core'; import {MatPaginator} from '@angular/material/paginat ...

The observable HTTP map appears to be more of a representation rather than a concrete entity

I seem to be struggling with understanding Typescript I expected the returned observable to have a method getTitle(), but it seems to be missing. Instead, I am getting an object that resembles AttachableContentModel without that particular method. What is ...

Guide on developing API endpoints with Rails and incorporating Swagger documentation for nested resources

In my Rails application, I am utilizing Active Record serializers to handle responses in JSON or HTML for the purpose of creating a public API. For basic authentication, I am using Devise Simple HTTP. To document my APIs, I have incorporated Swagger docs ...