Proper Input Component Typing in React/Typescript with MobX

Looking to create a custom React Input component using Typescript for MobX that requires the following input props:

  • a mobx store stateObject
  • a key from that store stateKey

I want Typescript to ensure that stateObject[stateKey] is specifically of type string. This is what the ExtraProps should handle in the given example. Unfortunately, I'm facing issues with the types not compiling as expected and receiving the error message:

TS2322: Type 'string' is not assignable to type 'K extends K ? string : any'.

Feel free to check out my related SO post here

Here is how I've approached it:

import React, {ChangeEvent, Component, InputHTMLAttributes} from "react";

type ExtraProps<T extends string | number | symbol, K extends T> = {
    stateKey: K,
    stateObject: { [k in T]: k extends K ? string : any }
}
type InputProps = Omit<InputHTMLAttributes<HTMLInputElement>, "value" | "onChange">
type InputPropType<T extends string | number | symbol, K extends T> = InputProps & ExtraProps<T, K>

export class InputField<T extends string | number | symbol, K extends T> extends Component<InputPropType<T, K>, any> {
    constructor(props: InputPropType<T, K>) {
        super(props);
        this.onChange = this.onChange.bind(this)
    }

    onChange(event: ChangeEvent<HTMLInputElement>) {
        let val: K extends K ? string : any = event.target.value; // TS2322: Type 'string' is not assignable to type 'K extends K ? string : any'.
        this.props.stateObject[this.props.stateKey] = val;
    }

    render() {
        let {stateKey, stateObject, ...props} = this.props;
        return <input {...props} value={stateObject[stateKey]} onChange={this.onChange}/>
    }
}

My questions would be:

  • what could be causing the issue with my types?
  • Is there a more efficient way to achieve my objective rather than the current approach?

Ultimately, I'm aiming for a controlled input component that seamlessly interacts with the mobx store for data management.

Answer №1

If you're looking for a solution, try implementing the following TypeScript code:

// Delve into TypeScript sorcery. Check out:
// https://medium.com/dailyjs/typescript-create-a-condition-based-subset-types-9d902cea5b8c
// to understand the magic behind it.
export type FilterFlags<Base, Condition> = { [Key in keyof Base]: Base[Key] extends Condition ? Key : never };
export type AllowedNames<Base, Condition> = FilterFlags<Base, Condition>[keyof Base];

To use this code snippet:


type ExtraProps<T> = AllowedNames<T, string>;
type InputProps = Omit<InputHTMLAttributes<HTMLInputElement>, "value" | "onChange">
// I'm unsure about the original '&', so I omitted it:
type InputPropType<T> = InputProps | ExtraProps<T>;

This will create a union type with the list of string properties under K. You don't actually need type K; rather, you just require type T, which represents the type of your stateObject value.

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

Issue with playing audio file using HowlerJS

Having trouble playing a .mp3 file in my project directory with Howler. I'm not sure if there's an error in my src. When I tried playing an online hosted audio file, it worked fine. I've placed the audio file in the same directory as Slideon ...

The module '@angular/core' could not be located in the '@angular/platform-browser' and '@angular/platform-browser-dynamic' directories

Attempting to incorporate Angular 2.0.0 with JSMP, SystemJS, and TS Loader in an ASP.NET MVC 5 (non-core) application. Encountering errors in Visual Studio related to locating angular modules. Severity Code Description Project File Line Suppr ...

Unveiling the seamless integration of TypeScript with webpack/metro mainFiles module resolution

Scenario Setup In Webpack, the mainFiles module resolution feature allows for resolving specific files based on the environment. This concept is exemplified by: | Button | | - index.ts | | - index.performer.ts | | - index.customer.ts // page.ts im ...

What is the best way to retrieve a soft deleted entity from typeorm in a postgreSQL database?

Can anyone help me figure out how to retrieve soft deleted documents from a PostgreSQL database using TypeORM's find, findOne, or query builder get/getMany methods? I keep getting undefined as the result. Is there a way to access these deleted values? ...

Issue encountered in Ionic/React/Typescript - Incorrect props supplied to React.FC<'erroneous props provided here'>

Having struggled with this issue for a while now without any success, I have searched through numerous questions here but none seem to address my specific case. Therefore, I kindly request your assistance. I am encountering difficulties passing props thro ...

Is it feasible to create a set of standardized values for an array's properties?

My goal is to restrict the values for a property (or react props in this case) based on the values provided in another property. Firstly, I have my Option interface: interface Option { value: string; label: string; } Next, I define my SelectInputProp ...

Unable to display toast notification in React/MUI/Typescript project

Currently tackling error notifications targeted at 400,401, and 500 errors within a large-scale project. I am facing an issue where I want to integrate my ErrorToastNotification component into my layout.tsx file to avoid duplicating it across multiple page ...

What is the best way to pass a specific property from a parent component to a child component in Angular when a button is clicked?

Hey there, I'm looking for a way to pass a single property (groupId) from a parent component to a child component. In this case, my child component is using ngx-bootstrap modal. Is there a solution available for this scenario? Essentially, I need to i ...

Having trouble launching the node pm2 process manager on AWS Elastic Beanstalk, npm update verification did not pass

Having some issues with utilizing pm2 for managing processes in my typescript node application, deployed on elasticbeanstalk. Whenever a new instance is launched by pm2, the following shows up in the logs ---------------------node.js logs---------------- ...

Tips for adding a "Select All" feature to a dropdown list?

Currently, I have a dropdown list with a filter for IN and OUT values. The functionality is working as expected: <select class="form-select" style="max-width: 100px" [ngModel]="selectedBrand" (ngModelChange)="onChangeT ...

How to efficiently manage multiple input fields with a single ref in React using TypeScript

I'm attempting to use the same reference for multiple input fields in my form. However, when I log it, the ref only points to the first input field. Is there a way I can share the same ref across different inputs? import React, {FC, useEffect, useRef, ...

Using ngIf for binding

Trying to bind values based on conditions specified in *ngIf. However, when using the && operator within *ngIf, it seems to be behaving mysteriously. Sample Code: <div *ngIf="days.sunday == true"> <p class="circle ml-3" ...

Using class-validator in Node.js to validate arrays of objects

My goal is to verify the Alcohol ID and Alcohol Name for emptiness. Below is the format I am working with: { "barId": "string", "barName": "string", "drinksItems": [ { "alcoholId": "string", "alcoholName": "string", "mixerLis ...

I'm having trouble retrieving the information as it is showing as undefined. Can anyone offer any advice?

Attempting to extract specific information from an API response has proven challenging. Despite my efforts to isolate the desired data, all listed details appear as undefined. import { HttpClient } from '@angular/common/http'; import { Injectable ...

Passing asynchronous data from method1 to method2 without impacting the functionality of the script responsible for fetching the asynchronous data in method1

When working with TypeScript, I encountered an issue while trying to invoke an external script called SPCalendarPro within a private method that asynchronously fetches data. The script is invoked in the following manner: private _getSPCalendarPro() { con ...

Guide to managing MUI's theme typography font weight choices with TypeScript

I am interested in learning how to incorporate a new font weight into my theme, for example, fontWeightSemiBold, and disable the existing fontWeightLight and fontWeightMedium. I believe this can be achieved through module augmentation. For reference, there ...

Evaluating file selection and uploading functionality within Spectron

Currently, I am faced with the challenge of writing a test for an electron GUI that includes a choose file dialog. Unfortunately, I do not have access to the inner workings of the GUI. Here is the code snippet I have written: await app.client.chooseFile( ...

Uploading images using multipart in react is causing an error and cannot be completed

Trying to upload images in the database using multipart is causing an error from the API saying 'Files can't be uploaded". Checking the API in postman shows it is working fine there. There seems to be an issue with my code, but I can't ...

Using Angular 7 to set the value of an object returned by an observable to a variable

I encountered an issue while trying to assign the return value from a service to a component. ngOnInit() { this.getInspectionData(); } getInspectionData() { this.auctionService.getInspectionResult(`df570718-018a-47d8-97a2-f943a9786536`) ...

Understanding the limitations of function overloading in Typescript

Many inquiries revolve around the workings of function overloading in Typescript, such as this discussion on Stack Overflow. However, one question that seems to be missing is 'why does it operate in this particular manner?' The current implementa ...