How can I suggest the return type of a function that is out of my control?

When I attempt to parse a JSON-formatted string, a linter error is triggered:

let mqttMessage = JSON.parse(message.toString())

// ESLint: Unsafe assignment of an `any` value. (@typescript-eslint/no-unsafe-assignment)

Given that I am in control of the content of message, I'm interested in explicitly informing TypeScript that the result of JSON.parse() is actually an Object. Is there a recommended approach for achieving this?

Please note that although I could suppress the warning, I prefer to explore alternative solutions to address the issue.

Answer №1

One issue is that when using JSON.parse, it returns a type of any.

This makes sense because TypeScript cannot determine if the parsed result will be a string, number, or object.

Your linting rule advises against assigning variables as 'any'.

One way to handle this is by coercing the result of JSON.parse:

type SomeObjectIKnowAbout = {

}; 
const result = JSON.parse(message.toString()) as SomeObjectIKnowAbout; 

A better approach in such cases would be to create a dedicated parsing function that validates the shape of the object at runtime and performs the necessary type casting for easier handling:

type SomeObjectIKnowAbout = {
    userId: string; 
}

type ToStringable = {
    toString: () => string; 
}

function parseMessage(message: ToStringable ) : SomeObjectIKnowAbout {
    const obj = JSON.parse(message.toString()); //It is unclear why you are parsing after calling toString.

    if (typeof obj === 'object' && obj.userId && typeof obj.userId === 'string') {
        return obj as SomeObjectIKnowAbout; 
    }
    else {
        throw new Error ("message was not a valid SomeObjectIKnowAbout"); 
    }
}

Answer №2

JSON.parse is not a one-size-fits-all solution, so providing a generic argument is not possible.

You have a couple of choices to consider.

A straightforward approach would be to specify the type of the variable that you are assigning the result of JSON.parse to, since it returns any:

let mqttMessage: MQTTMessage = JSON.parse(message.toString());

(I've used MQTTMessage as an example type.)

However, this method may not be robust enough for all cases, as it assumes the string contains the expected data type and may necessitate repeated assumptions elsewhere in your code.

Alternatively, you could create a function:

function parseMQTTMessageJSON(json: string): MQTTMessage {
    const x: object = JSON.parse(json);
    if (x && /*...appropriate checks for properties here...*/"someProp" in x) {
        return x as MQTTMessage;
    }
    throw new Error(`Incorrect JSON for 'MQTTMessage' type`);
}

Then you can use the function in your code like this:

let mqttMessage = parseMQTTMessageJSON(message.toString());

Answer №3

Instead of relying on type assertions and runtime wrapper functions, you have the option to employ declaration merging to enhance the global JSON object with a generic overload for the parse method. This enhancement allows you to specify the expected type and get better IntelliSense support if you utilize a reviver during parsing:

interface JSON {
    parse<T = unknown>(text: string, reviver?: (this: any, key: keyof T & string, value: T[keyof T]) => unknown): T
}

type Test = { a: 1, b: "", c: false };

const { a, b, c } = JSON.parse<Test>(
    "{\"a\":1,\"b\":\"\",\"c\":false}",  
    //k is "a"|"b"|"c", v is false | "" | 1
    (k,v) => v
);

Alternatively, if you prefer using declaration files to extend global interfaces:

declare global {
  interface JSON {
    parse<T = unknown>(text: string, reviver?: (this: any, key: keyof T & string, 
  value: T[keyof T]) => unknown): T
  }
}

Playground

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 error message "Alamofire JSON POST response failed: Lost connection to the network" was displayed

Many individuals encounter this error for various reasons. I have yet to come across a solution that resolves my specific issue. In order to log my Alamofire requests, I utilize Timberjack. While all of my GET requests function properly and return JSON d ...

What is the process for obtaining a subset of appointments from a FHIR server?

Whenever I access JSON objects from a FHIR server that follow this format, they have the following structure: "resourceType": "Appointment", "id": "example", "text": { "status": "ge ...

Is the Typescript index signature limited to only working with the `any` type?

I recently made changes to my interface and class structure: export interface State { arr : any[]; } export const INITIAL_STATE: State = { arr: [] }; This updated code compiles without any issues. Now, I decided to modify the Interface to incl ...

python scraping: how to extract two nested elements

Hello, I am looking to extract information from the < del> and < ins> tags below. However, I haven't been able to find a solution yet. Does anyone have any ideas on how to scrape this data? This is my Python code: import requests import j ...

Bring in TypeScript property from an external scope into the current scope

I am encountering an issue with my TypeScript code. Inside the anonymous functions, I am unable to change the properties of the class because they are out of scope. Is there a way to pass them in so that they can be modified? class PositionCtrl { ...

Issues with string encoding after using NSUTF8StringEncoding

When attempting to download a JSON string from a server, I noticed that the formatting is not as expected. Apologies for the confusion, I am new to using Stack Overflow. Upon comparing the received string with the var_dump output from PHP, I observed that ...

Using Sails.js to display JSON data retrieved from an HTTPS request in the view

Just getting the hang of Sails.js, so any help is appreciated. I've used an XML service and successfully converted it to JSON using xml2js var req = https.request(options, function(res) { var xml = ''; res.on('data', fun ...

Is there a way to output several lines from a JSON file in print form?

I'm working with HTML code that displays multiple lines from a JSON file, but it only shows one line at a time. How can I modify the code to display all users' information? <!DOCTYPE html> <html> <head> <script> function ...

retrieving a single object from $resource by passing a non-ID parameter

I am utilizing $resource to retrieve an array of objects. The method I am invoking is as follows: fetchTeamResource(): ng.resource.IResourceClass<ITeamResource> { return this.$resource("/api/Teams:teamId"); } Below is how I am i ...

Converting data tables to JSON format

Hey, how's it going? I have this dataframe that resembles a nested table and I'm aiming to convert it into a JSON format (kind of like a matryoshka doll). I'm working with Python. Here's an example: Dataframe: id name relations ...

Encountered an error while attempting to compare 'true' within the ngDoCheck() function in Angular2

Getting Started Greetings! I am a novice in the world of Angular2, Typescript, and StackOverflow.com. I am facing an issue that I hope you can assist me with. I have successfully created a collapse animation for a button using ngOnChanges() when the butto ...

Struggling with creating a generic TypeScript structure?

My goal is to manipulate data structured like this: const exampleState = { elements : { element1: { values: { value1: 10, value2: 10, }, elementDetails : { detail1 : { values: { value1: ...

Attempting to extract information from JSON object provided in the request body

When it comes to working with APIs, especially those using JSON, I consider myself a novice. Here is a snippet of my code... Endpoint: [HttpPost("postWithBody")] public async Task<IActionResult> PostWithBody(string param1, [FromBody] object reque ...

Guide on how to showcase the template by leveraging the roomList information with ngTemplateOutlet in Angular

TS roomList = [{ name: 'Room2' }] HTML <div class="Layout-body"> <ng-container *ngFor="let dt of roomList; index as i" [ngTemplateOutlet]="Room1" [ngTemplateOutletContext]="{ data: dt, i: i }&qu ...

Generate an interactive rain accumulation graph by combining multiple data points of rainfall into a dynamic Highcharts chart

I am looking to create a highchart that displays rain data along with the accumulated rain for the visible data points in the chart (where the accumulated rain series will always remain at 0), similar to this example image: To populate the static series f ...

Why isn't it possible to send POST data to a JSON file using JQuery/AJAX?

I'm currently learning how to use JQuery/Ajax from a helpful tutorial on YouTube. To watch the video, simply click here. While I can successfully retrieve data from the order.json file, I encounter an error whenever trying to send POST requests. Bel ...

Uploading Files with Typescript Promises

Hello everyone, I'm facing an issue where a dialog window is opening before all the files are uploaded to the server. Can anyone please guide me on what might be going wrong in my code? public UploadAll() { this.doAsyncTask().then(() => ...

What is the procedure for configuring environmental variables in a JSON file? (e.g., client secrets, client IDs, etc

Inside my client-secret.json file, the content looks something like this: { "web": { "client_id": "asdfjasdljfasdkjf", "client_secret": "1912308409123890", "redirect_uris": ["https://www.example.com/oauth2callback"], "auth_uri": "https:/ ...

Using immer JS to update a nested value has been successfully completed

What is the most efficient way to recursively change a value using a recursive function call in a Produce of Immer? The WhatsappState represents the general reducer type, with Message being the message structure for the application/db. type WhatsappState = ...

Using React.Fragment in VS Code with TypeScript error 2605 while having checkJs enabled

While utilizing the JS type checking feature in VScode, I encountered an issue with React.Fragment that is being linted with an error: JSX element type 'ReactElement<any>' is not a constructor function for JSX elements. Type 'ReactEle ...