Creating a Jsonifiable type that aligns with static interfaces: A step-by-step guide

When working with Typescript, there are 3 types that share similarities as they are composed of primitive types, maps, and arrays:

type Color1 = { [component: string]: number }

type Color2 = {
  green: number
  red: number
  blue: number
}

interface Color3 {
  green: number
  red: number
  blue: number
}

Is it possible to create a type that encompasses all of the definitions above?

The straightforward approach does not cover Color3:

type JsonPrimitive = string | number | boolean | null
type JsonifiableObject = { [Key in string]?: Jsonifiable }
type JsonifiableArray = readonly Jsonifiable[]
type Jsonifiable = JsonPrimitive | JsonifiableObject | JsonifiableArray

const color1 = {green: 1, red: 2, blue: 3} as Color1 as Jsonifiable
const color2 = {green: 1, red: 2, blue: 3} as Color2 as Jsonifiable
const color3 = {green: 1, red: 2, blue: 3} as Color3 as Jsonifiable

This results in:

Conversion of type 'Color3' to type 'Jsonifiable' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.
  Type 'Color3' is not comparable to type 'JsonifiableObject'.
    Index signature for type 'string' is missing in type 'Color3'.

Answer №1

It seems that there is no specific type available that functions in the desired manner. If you have an object type with a string index signature such as JsonifiableObject (which should be defined as {[k: string]: Jsonifiable} or possibly

{[k: string]: Jsonifiable | undefined}
, depending on whether you truly want to utilize the ? modifier), then it will not align with an interface that lacks an index signature. Interfaces do not automatically come with implicit index signatures. This behavior is detailed in microsoft/TypeScript#15300 and is intentional.

You will either need to concede or find a workaround for this issue.


If you require an interface type to match something like this, you must modify the index signature to use a generic key instead. This means transforming your Jsonifiable type into a JsonifiableValidate<T> type where

T extends JsonifiableValidate<T>
only if T is valid. It essentially becomes a generic constraint:

type JsonifiableValidate<T> = T extends JsonPrimitive ? T :
    T extends readonly (infer A)[] ? readonly (JsonifiableValidate<A>)[] :
    T extends Function ? never :
    T extends object ? { [K in keyof T]: JsonifiableValidate<T[K]> } : never;

Since generic types lack type argument inference (a known missing feature outlined in microsoft/TypeScript#32794), you will need to create a generic helper identity function like so:

const jsonifiable = <T,>(t: JsonifiableValidate<T>) => t;

Instead of using const color: Jsonifiable = {⋯}, you would write const color = jsonifiable({⋯}) like this:

const color = jsonifiable({ green: 1, red: 2, blue: 3 } as Color3); // fine

Access the code via the provided Playground link.

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

Embedding a TypeScript React component within another one

Currently, I'm facing an issue with nesting a TypeScript React component within another one, as it's causing type errors. The problem seems to be that all props need to be added to the parent interface? Is there a way to handle this situation wi ...

Matching TypeScript values and types does not function as intended

Recently, I delved into TypeScript to work on my new AngularJS project. However, I encountered an issue where the id, which is supposed to be of type number, is actually being treated as a string. Have I overlooked something in my code? interface IRout ...

I am currently experiencing a problem with deleting documents in Firebase Firestore using React, Typescript, and Redux. Despite the code running, the document continues to exist in

I seem to be encountering an issue that I can't quite figure out. Despite my code running smoothly, I am unable to delete a document from Firestore. The document ID definitely exists within the database, and there are no subcollections attached to it ...

Enable the Angular button only when the radio button has been selected

Just starting out with Angular and I have a query. This is a scenario for my project at work. https://i.stack.imgur.com/R3SxA.png In this screenshot, I want to enable the "ajouter" button when at least one radio button is selected in the secure chest (s ...

Hold on until the page is reloaded: React

My current setup includes a React Component that contains a button. When this button is clicked, a sidePane is opened. What I want to achieve is refreshing the page first, waiting until it's completely refreshed, and then opening the sidepane. Below i ...

Loading and unloading an Angular 6 component

One challenge I'm facing involves creating an image modal that appears when an image is clicked. Currently, I have it set up so that the child component loads upon clicking the image. However, the issue is that it can only be clicked once and then dis ...

Utilizing Angular Services to Share Events and Reusing Components Multiple Times on a Page

My unique custom table is made up of multiple components, each emitting events using a shared service called TableEvent. These events are subscribed to by a class named TableTemplate, which facilitates communication among the different components of the ta ...

Is there a way to use Lodash to quickly return the same value if a condition is met using the ternary operator

Is there a condensed way to do this using Lodash? (or perhaps Vanilla JS/TypeScript) var val = _.get(obj, 'value', ''); Please note that var val = obj.value || ''; is not suitable as it considers 0 and false as valid but fal ...

Tips on sending various properties to makeStyles() using TypeScript

After learning how to pass 1 prop to makeStyle() from a resource, I decided to try passing in 2 props for my project. However, I encountered an error stating cannot find name 'props'. Any assistance on this issue would be greatly appreciated! con ...

Identify the category of the component

Using a Link component from version 4.0.0-beta.2, I am exploring its capability to override the root element with a field called component. My goal is to wrap the Link component in a new component called MyLink and pass a custom component through props: ...

Using href with IconButtonProps is not supported

I'm facing a challenge in creating a wrapper for the IconButton. I aim to pass components or href props, but unfortunately, I am unable to achieve this by passing the IconButtonProps. Is there a way to accomplish this? function CustomIconButton(props ...

Managing Prisma error handling in Express

Dealing with error handling using ExpressJS and Prisma has been a challenge for me. Anytime a Prisma Exception occurs, it causes my entire Node application to crash, requiring a restart. Despite looking at the Prisma Docs and doing some research online, I ...

Exploring Iteration of TypeScript Arrays in ReactJS

Issue Encountered: An error occurred stating that 'void' cannot be assigned to type 'ReactNode'.ts(2322) The error is originating from index.d.ts(1354, 9) where the expected type is related to the property 'children' defi ...

Which symbol or character corresponds to the public "get symbol" method?

While going through some Typescript code, I came across a line that is giving me trouble to understand. const symbol = Symbol.for('symbolname'); class Something { public get [symbol]() { return true; } ... } I am confused abou ...

Troubleshooting Next.js and Tailwind CSS Breakpoints: What's causing the

Having trouble with my custom breakpoints. For instance, I attempted the following: <div className="flex flex-row gap-5 mb-5 md:ml-15 sm:ml-15"> ... </div> The margin is not being applied on small and medium screens. Here are the th ...

Utilizing Sharepoint Online SPFX Web parts with React: A guide to selecting scripts dynamically based on environment requirements

Would it be possible for me to dynamically choose which script to utilize in my web component? This is how I currently have my imports set up: import * as jQuery from 'jquery'; import 'jqueryui'; Here's what I am aiming to achie ...

Presentation of information with loading and error scenarios

How can we effectively display data in an Angular view, considering loading state and error handling? Imagine we are fetching a set of documents from our backend and need to present them in an Angular view. We want to address three possible scenarios by p ...

Finding the index of a chosen option in Angular

Attempting to retrieve the index of the selected option from a select element using Angular. The Angular (4) and Ionic 3 frameworks are being utilized. The template structure is as follows: <ion-select [(ngModel)]="obj.city"> <ion-option ...

What could be causing the error message "why can't shows read property

Within my Ionic-Angular application, I have successfully loaded the content from the database and printed it on the console. However, I am facing an issue where I cannot bind this content to the view. The error message that appears is displayed in https:// ...

What exactly does "context" refer to in the context of TypeScript and React when using getServerSideProps?

As a beginner in coding, I have a question regarding a specific syntax I recently encountered. I am confused about this particular line of code and couldn't find any helpful explanations online: export async function getServerSideProps(context: GetSer ...