What is the process of importing JSON data into a TypeScript Map<K, V> type?

I'm facing a challenge in importing JSON data that may include mappings which could be either present or absent within one of the properties. I initially thought that using Map<string, number> as the correct data type to represent this scenario would work, but unfortunately, I encounter an error when attempting to do so.

The structure of my JSON file, named data.json, is shown below:

{
    "datas": [
        {
            "name":"test1",
            "config":"origin",
            "entries": {
                "red":1,
                "green":2
            }
        },
        {
            "name":"test2",
            "config":"remote",
            "entries": {
                "red":1,
                "blue":3
            }
        },
        {
            "name":"test3",
            "entries": {
                "red":1,
                "blue":3,
                "purple":3
            }
        }
    ]
}

The TypeScript code in Data.ts responsible for reading this data appears like this:

import data from './data.json';

export class Data {
    public name:string;
    public config:string;
    public entries:Map<string, number>;
    constructor(
        name:string,
        entries:Map<string, number>,
        config?:string
    ) {
        this.name = name;
        this.entries = entries;
        this.config = config ?? "origin";
    }
}

export class DataManager {
    public datas:Data[] = data.datas;
}

However, the final line,

public datas:Data[] = data.datas;
, ends up causing an error.

This raises the question - what is the proper way to import such data?

The main objectives here are threefold:

  1. Any time entries is present, it should undergo some form of validation to ensure it only consists of properties of type number. The specific properties remain unknown to the programmer, but will be significant to the end user.
  2. If config is missing in the JSON file, the creation of Data objects should default to a preset value (in this case, "origin")
  3. The process of assigning the data must be streamlined and free of unnecessary boilerplate code. In the event that Data gains a new property in the future (and there may or may not be corresponding updates to the data within Data.json), there shouldn't be a need to alter how DataManager.data fetches the values

Is this feasible, and if so, what is the right approach to writing code that achieves these objectives?

Answer №1

If you're looking for a minimalistic approach, consider avoiding the use of classes for your data in JavaScript. Instead, opt for plain JavaScript objects and define strong types for your specific needs. For instance, rather than creating a Data class, you can define an interface. Similarly, instead of utilizing instances of the Map class with string keys, you can simply use an object with a string index signature to represent your existing data structure:

interface Data {
    name: string;
    config: string;
    entries: { [k: string]: number }
}

To create a valid instance of Data, there's no need to invoke the new operator; just construct an object literal with properties like name, config, and entries of appropriate types. The entries property indicates that the keys are strings and the values associated with those keys should be numbers.


Now, let's transform data.datas into an array of type Data[] while fulfilling your specified criteria:

const datas: Data[] = data.datas.map(d => ({
    config: "origin", // set default values
    ...d, // merge object properties    
    entries: onlyNumberValues(d.entries ?? {}) // filter non-numeric entries
}));

function onlyNumberValues(x: { [k: string]: unknown }): { [k: string]: number } {
    return Object.fromEntries(
        Object.entries(x).filter(
            (kv): kv is [string, number] => typeof kv[1] === "number"
        )
    );
}
  1. The code snippet above updates the entries property by filtering the existing entries based on criteria. If entries doesn't exist, an empty object is used. This logic is implemented in the onlyNumberValues() function by leveraging methods like Object.entries(), user-defined type guards, and Object.fromEntries().

  2. In cases where certain properties might be missing from the JSON data, setting default values is essential. By starting with default properties in an object literal and then spreading properties from the JSON object (as demonstrated with the config property), you ensure that any additional properties in the JSON object will also be included in the final result.

  3. As new properties introduced in the JSON object are automatically merged, remember to specify defaults if needed. This way, the resulting object will contain all necessary properties along with their respective values.

Lets verify the functionality:

console.log(datas)

/* [{
  "config": "origin",
  "name": "test1",
  "entries": {
    "red": 1,
    "green": 2
  }
}, {
  "config": "remote",
  "name": "test2",
  "entries": {
    "red": 1,
    "blue": 3
  }
}, {
  "config": "origin",
  "name": "test3",
  "entries": {
    "red": 1,
    "blue": 3,
    "purple": 3
  }
}] */

Looks promising.

Playground link to view code

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

The JMeter JSON Assertion does not allow paths to end with a dot or two dots

I'm attempting to replicate the JMeter script outlined in a blog post found at this link -- specifically focusing on the section titled "Front End: Testing Custom Communication Channels". The author discusses the setup of JSON assertions to validate ...

transition from mapStateToProps to using hooks

Just dipping my toes into the world of React (hooks) and learning by writing code. I'm grappling with converting MapStateToProps to hooks, specifically stuck on one part just before 'currentItem'. Here's the original code snippet: co ...

Why does Postman convert decimals to integers when displayed in "Pretty" view?

When working with a JSON response from a REST API call in Postman, I noticed that the "Raw" view displays values as decimals while the "Pretty" view shows them as integers. Why is this difference? I am specifically interested in the value {"number":100.00 ...

What is the best way to handle both json and jsonp responses in a node.js + Express application?

I have a REST API service built on Node.js + Express, and it currently only supports responses in JSON format. Now I am looking to add support for JSONP responses so that users can obtain them by appending alt=JSONP to the request string. My question is, ...

Node and Express app making a double POST request

This particular section serves as the route code. router.post('/content', function(req, res) { var topic = req.body.inputText; console.log("topic :"+topic); //displays correctly var somedata = ""; console.log("request arrived for ...

Using Javascript to find, search for, and listen for timeupdate events with Mux

I am currently delving into the intricacies of the timeupdate, seeking, and seek events. Utilizing MUX Player, I notice that these events are triggered, but comprehending their workings is proving to be quite a challenge. For instance, in the provided vide ...

Can a WCF service utilize both the SOAP and JSON methods simultaneously?

I recently started working with WCF, and I need to utilize the same WCF for bothSOAPandJSON` formats using the post method. Is this achievable? Any guidance would be greatly appreciated. ...

The forwarded reference of the material-ui component is consistently empty

Currently, I am exploring the creation of a navbar with a drawer element using the material-ui library. I am attempting to pass the <Drawer> Component to the parent navbar so that I can access the toggleDrawer function within my NavDrawer component. ...

Disable the yellow curly error lines in Visual Studio Code

Currently, I am utilizing VSCode with ESlint for Typescript development. I'm curious about how to turn off or remove the yellow curled error lines in my code editor, like the ones displayed in this example image: https://i.stack.imgur.com/Zdtza.png M ...

Exploring TypeORM: Leveraging the In() function within @ManyToMany relationships

There are two main characters in my story: Hero and Villain (their profiles are provided below). How can I utilize the Encounter() method to identify all instances where the Hero interacts with the Villain based on an array of Villain IDs? I am seeking a ...

Handling Click and Mouse Events with React [react-sortable-hoc, material-ui, react-virtualized]

I have come across an interesting example that I would like to share with you. Check out this live working example on Stackblitz When the delete button on the red bin icon is pressed, the onClick event handler does not get triggered (sorting happens inst ...

Extract nested JSON data and load it into a pandas DataFrame

How can I convert the provided Json data into a pandas DataFrame? The JSON is nested and contains multiple lists and dictionaries. { "status": "success", "data": { "resultType": "vector", ...

Encountering a Problem with HTTP Requests in Angular 2

Seeking assistance with a technical issue. My objective: Make a REST API call to retrieve JSON data and resolve an Angular 2 promise. ServerAPI built with Node.js/ExpressJS/Lodash Sample of server.js file: var express = require('express'); va ...

Where should business logic be situated in ServiceStack applications?

I've encountered an issue with a class that extends the Service class in the ServiceStack library. When I create a separate class that inherits from Service and define the Get or Any methods inside it, everything works fine. However, the problem arise ...

Restricting the frequency at which a specific key value is allowed to appear in JSON documents

Within my JSON file, there is an array structured like this: { "commands": [ { "user": "Rusty", "user_id": "83738373", "command_name": "TestCommand", "command_reply": "TestReply" } ] } I have a requirement to restrict the num ...

Specify the return type based on specific parameter value

I'm facing a situation where I have two definitions that are identical, but I need them to behave differently based on the value of the limit parameter. Specifically, I want the first definition to return Promise<Cursor<T>> when limit is g ...

transforming an array into JSON structure using node.js

Hello, I have a list of data that I need to convert into JSON format. Here is an example of how I want the data to look: [ { "slideName": "s0", "imageUrl": "https://s3.amazonaws.com/lifestyle345/testing/slides/cbaa5e650152a0332b494f0074985 ...

NextJS 13 causes tailwind to malfunction when route group is utilized

I've encountered an issue in my NextJS 13 application where Tailwind classes are no longer being applied after moving page.tsx/layout.tsx from the root directory to a (main) directory within the root. I suspect that there may be a configuration that i ...

Frozen objects in Typescript 2 behave in a variety of ways depending on their shape

Let's say I'm working with an object whose inner structure is unknown to me because I didn't create it. For instance, I have a reference to an object called attributes that contains HTML attributes. I then made a shallow copy of it and froze ...

Tips for resolving the error message when a JSON file is unable to open stream

I encountered this particular error message: Warning: file_get_contents(): failed to open stream: HTTP request failed! HTTP/1.1 404 Not Found If you are curious, the contents of that link are as follows: The page only displays this line: {"status_c ...