Using Typescript to pass an interface as an argument to a function that requires a JSON type

Here is an extension related to the topic of Typescript: interface that extends a JSON type

Consider the following JSON type:

type JSONValue = 
 | string
 | number
 | boolean
 | null
 | JSONValue[]
 | {[key: string]: JSONValue}

The goal is to inform typescript that interfaces matching the JSONType should be automatically casted when passed into functions accepting the JSONType. For instance:

interface Foo {
  name: 'FOO',
  fooProp: string
}

const bar = (foo: Foo) => { return foo }

const wrap = (fn: (...args: JSONValue[]) => JSONValue, args: JSONValue[]) => {
  return fn(...args);
}

wrap(bar, {name: 'FOO', fooProp: 'hello'});

However, this currently results in an error stating:

Argument of type '(foo: Foo) => Foo' is not assignable to parameter of type '(...args: JSONValue[]) => JSONValue'.
  Types of parameters 'foo' and 'args' are incompatible.
    Type 'JSONValue' is not assignable to type 'Foo'.
      Type 'null' is not assignable to type 'Foo'.

even though logically we know that the inputted foo adheres to JSON standards.

You can play around with this code on the TypeScript Playground here

Is there a way for typescript to acknowledge that this interface is indeed a valid JSON type?

Answer №1

Your current implementation has several issues that need to be addressed. The function wrap appears to expect a function fn that can handle any type of data represented by JSONValue[], but you are passing in a function bar that only accepts Foo. To resolve this, consider making the wrap function generic over T, where T represents the parameter types of fn. This way, you can ensure that args align with the expected types when calling fn.

I have modified args to be a spread parameter so that it does not rely on tuple types, as you do not pass tuples when invoking the function.

const wrap = <T extends JSONValue[]>(
  fn: (...args: T) => JSONValue, 
  ...args: T
) => {
  return fn(...args);
}

Even after these changes, there is still an issue with the usage of Foo as an interface. Interfaces do not have implicit index signatures, which poses a problem when trying to match the constraint {[key: string]: JSONValue}. To work around this without broadening the JSONValue type, consider converting Foo into a type.

type Foo = {
  name: 'FOO',
  fooProp: string
}

const bar = (foo: Foo) => { return foo }

const wrap = <T extends JSONValue[]>(
  fn: (...args: T) => JSONValue, 
  ...args: T
) => {
  return fn(...args);
}

wrap(bar, { name: 'FOO', fooProp: 'hello'});

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 parameter cannot be assigned a value of type 'string' as it requires a value that matches the format '`${string}` | `${string}.${string}` | `${string}.${number}`'

I recently updated my react-hook-forms from version 6 to version 7. Following the instructions in the migration guide, I made changes to the register method. However, after making these changes, I encountered the following error: The argument being pass ...

What could be causing my TypeScript code to not be recognized as CommonJS?

I rely on a dependency that is transpiled to ES6. My goal is to leverage ES2019 features in my own code. Ultimately, I aim to output ES6. This is how I set up my tsconfig { "compilerOptions": { "module": "CommonJS" ...

Issue occurred while trying to deploy Firebase functions following GTS initialization

Objective: I am aiming to integrate Google's TypeScript style guide, gts, into my Firebase Functions project. Desired Outcome: I expect that executing the command firebase deploy --only functions will successfully deploy my functions even after init ...

Describe a Typescript Interface Value as a collection of strings or any input provided as an argument

Uncertain if the question title is accurate - currently developing react components within a library that has specific predefined values for certain properties. However, additional values may need to be added on a per usage basis. So far, this setup is fun ...

Preact: occasional occurrence of a blank page after refreshing

Starting out with Preact & TypeScript, I decided to kickstart my project using the parcel-preact-typescript-boilerplate. Initially, everything seemed to be working smoothly. However, I noticed that occasionally, especially on reload or initial page lo ...

Troubleshooting problem with refreshing URL on "ionic serve" mode

After transitioning my project from Ionic 2 to Ionic 3, I've encountered an issue with ionic serve and the rebuilding process. Initially, when I build the project, everything functions as expected. However, I've noticed that the URL in the brows ...

Using Material UI date picker with TypeScript: A Complete Guide

Well, I have to admit that I usually don't resort to putting 'any' as the type when I'm uncertain what to do, but right now, I'm starting to feel quite frustrated. I'm currently working with Material UI date picker in conjunct ...

What is the best way to assign the value of "this" to a variable within a component using Angular 2 and TypeScript?

In my component, I have the following setup: constructor() { this.something = "Hello"; } document.addEventListener('click', doSomething()); function doSomething(e) { console.log(this.something) // this is undefined } I am struggling to a ...

Creating custom functionality by redefining methods in Typescript

My current scenario is as follows: abstract class A implements OnInit{ ngOnInit() { this.method(); } private method() { // carrying out tasks } } class B extends class A implements OnInit { ngOnInit() { thi ...

Leveraging Expose in combination with class-transformer

I have a simple objective in mind: I need to convert the name of one property on my response DTO. refund-order.response.dto.ts export class RefundOrderResponseDto { @Expose({ name: 'order_reference' }) orderReference: string; } What I w ...

What is the process of sending SMS using PHP API without receiving a JSON response?

Using the code below to send SMS via a PHP API: $ch = curl_init("http://wpsms.whitepearldemo.biz....?user=".$user."&password=".$password."&msisdn=".$msisdn."&sid=".$sid."&msg=".$msg."&fl=".$fl."&gwid=".$gwid); $result = curl_exec($ ...

Encountering 'null' error in template with Angular 4.1.0 and strictNullChecks mode

After updating Angular to version 4.1.0 and activating "strictNullChecks" in my project, I am encountering numerous errors in the templates (.html) that look like this: An object may be 'null' All these errors are pointing to .html templat ...

Is it possible to create a single directive that can handle both Structural and Attribute behaviors?

Trying to develop an Angular Directive that will handle various functionalities based on config input values Dynamically add elements to the DOM based on input values (similar to ngIf) Apply styling to rendered elements Add attribute properties such as d ...

"Exploring the differences between normalization structures and observable entities in ngrx

I'm currently grappling with the concept of "entity arrays" in my ngrx Store. Let's say I have a collection of PlanDTO retrieved from my api server. Based on the research I've done, it seems necessary to set up a kind of "table" to store th ...

Serializing MongoDB, JSON objects, and dictionary members

Imagine having a class structured like this: class A { Dictionary<string, string> Dict1 { get; set } } Now, when serializing it to Json, you want the output to be: "A" : {"strKey1" : "strVal1", "strKey2" : "strVal2"} instead of: "A" : { "Dict ...

Is it possible in Java to convert a JSONObject to a JSONStringer and vice versa?

I'm feeling a bit puzzled about how to navigate through the apparent limitations of the JSONStringer class. I am aware that JSONStringer is designed to link together JSON rather than create it as a whole, but when a function only returns a JSONStringe ...

The issue with Angular 4 imports not refreshing in a .less file persists

Currently, I am in the process of developing a small Angular project that utilizes less for styling purposes. My intention is to separate the styling into distinct folders apart from the components and instead have a main import file - main.less. This fil ...

Adding information to a MySQL database table with Python

As a Python beginner, I am attempting to insert JSON file data into my database table using Python. However, despite not encountering any errors, I only receive the message: Tweet number 49634 is uploading to the server I'm struggling to identify wh ...

Why is it important to incorporate depth in JSON encoding?

Take a look at this illustrative code: $array = array( 'GiamPy' => array( 'Age' => '18', 'Password' => array( 'password' => '1234&apo ...

Error interpreting the response string

After attempting to extract values from a response by parsing it into separate strings, I encountered some difficulties. The response text looks like this: response = "Data1{ key1='4722**********6', key2='2107', ...