Compelled to utilize unfamiliar types in TypeScript generics

When working with a Typescript React form builder, I encountered a situation where each component had different types for the value and onChange properties. To tackle this issue, I decided to utilize generics so that I could define the expected types for each component.

Here is a simplified interface for the lowest level form question component:

interface FormFieldProps<T> {
  value: T;
  onChange: (value: { [key: string]: T }) => void;
}

And here is a simplified interface for the top-level FormBuilder component:

interface FormBuilderProps<T> {
    fields: FormFieldProps<T>[];
    onChange: (value: { [key: string]: T }) => void;
    formData: { [key: string]: T };
}

The challenge I faced was that I had to provide a generic type at the highest level in the FormBuilder, but only 'unknown' or 'any' seemed to be suitable options. This posed a problem as some components required 'string', 'string[]', 'number', etc.

To illustrate, here is a sample component definition:

const FormBuilder = (props: FormBuilderProps<unknown>) => {...}

Although I managed to eliminate type errors by passing down 'unknown' as the generic from the top-level FormBuilder component, all typings became 'unknown'. This led to situations where I could inadvertently pass a 'number' to a component expecting a 'string', for example.

Updated Statement:

An issue arises when attempting to set typed data for FormBuilderProps. The necessity of passing the generic type from top to bottom results in mismatches like 'boolean is not assignable to type string' for certain components. Should I manually handle each potential option for T and implement type guards in every component?

Each item within the 'fields' array is processed through a switch statement based on the componentType, leading to the rendering of the appropriate component. Utilizing these enums to refine the type of T would be advantageous.

export const formBuilderData: FormBuilderProps<string> = {
  fields: [
    {
      index: 1,
      columnName: 'columnName',
      componentType: PureComponentType.TEXT,
      value: 'alphabet',
    },
    {
      index: 2,
      columnName: 'bestBear',
      componentType: PureComponentType.SWITCH,
      value: true,
  ],
  readOnly: false,
  formData: {},
};

Answer №1

While my solution may not be as elegant as desired, it does get the job done.

1.) I removed the generic from the top-level interface

export interface FormBuilderProps {
  fields: FieldComponentTypes[];
  onChange: (value: { [key: string]: Pick<FieldComponentTypes, 'value'> }) => void;
  formData: { [key: string]: Pick<FieldComponentTypes, 'value'> };
}

2.) For individual field items, I kept the generic in place

export interface FormFieldProps<T> {
  index: number;
  columnName: string;
  value?: T;
  onChange?: (value: { [key: string]: Pick<FieldComponentTypes, 'value'> }) => void;
}

3.) To specify the type of component for each, I created interfaces for each component and passed the appropriate generic type. This way, the switch component knows exactly what type of component it is receiving.

export interface TextFieldComponent extends FormFieldProps<string> {
  componentType: PureComponentType.TEXT;
}

4.) Finally, I combined all my component interfaces into one

export type FieldComponentTypes =
  | AutoCompleteFieldComponent
  | DateFieldComponent
  | DropdownFieldComponent
  | MultiSelectFieldComponent
  | TextFieldComponent
  | SwitchFieldComponent;

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

Convert image file to a React TypeScript module for export

Imagine a scenario where you have a folder containing an image file and an index file serving as a public API. Is there a method to rename the image file before reexporting it? Here is the structure of the folder: └── assets/ ├── index.t ...

How to identify generic return type in TypeScript

My goal is to develop a core dialog class that can automatically resolve dialog types and return values based on the input provided. I have made progress in implementing this functionality, but I am facing challenges with handling the return values. Each ...

Converting a string value into an object in Javascript using a command that functions similarly to eval in Python

When working with Python, the stringValue variable is often assigned as a string: stringValue = '{"DATA":{"VERSION":1.1, "STATE":True, "STATUS":"ONLINE"}}' To convert this string into a Python di ...

An endless cascade of dots appears as the list items are being rendered

Struggling to display intricately nested list elements, Take a look at the JSON configuration below: listItems = { "text": "root", "children": [{ "text": "Level 1", "children": [{ "text": "Level 2", "children": [{ "text": ...

Error TS2339: The attribute 'scope' is not found in the 'JwtPayload' data type

Encountering an error in my IntelliJ IDE while trying to utilize decodedJwt.scope. The specific error message reads: Property 'scope' is not available on type 'JwtPayload'. Suggestions offered by IntelliJ include: sub, aud, exp, iat, ...

What is the method for extending props from React.HTMLProps<HTMLButtonElement>?

"react": "^17.0.2", "typescript": "^4.2.4" Can someone help me understand how to extend props from React.HTMLProps? import { FC, HTMLProps } from 'react' export interface SliderButtonProps extends HTMLPro ...

Issue: The data type 'void' cannot be assigned to the data type 'ReactNode'

I am encountering an issue while calling the function, this is just for practice so I have kept everything inside App.tsx. The structure of my class is as follows: enum Actor { None = '', } const initializeCard = () => { //some logic here ...

Typescript Regular Expression Issue: filter function is not returning any matches

Currently, I am developing an Ecommerce API and working on a class specifically for search queries. My approach involves using regex and typescript with node.js. Although I have based my project on a JavaScript node project, I am encountering an issue wher ...

Move the cache folder for NextJS to a new location

Is it possible to customize the location of the cache folder currently located in "./.next/cache"? I am interested in modifying this because I am developing an application that receives high traffic daily, and I plan to deploy multiple applications from m ...

Exploring the correct navigation of page objects through Protractor using TypeScript

I'm working on setting up a protractor test suite with TypeScript and running into an issue involving chaining of pageObjects for multiple pages. I haven't seen any examples that deal with this specific scenario. I've simplified the example ...

What is the best way to showcase the outcomes of arithmetic calculations on my calculator?

In the midst of creating a calculator, I have encountered some issues in getting it to display the correct result. Despite successfully storing the numbers clicked into separate variables, I am struggling with showing the accurate calculation outcome. l ...

Arranging arrays of various types in typescript

I need help sorting parameters in my TypeScript model. Here is a snippet of my model: export class DataModel { ID: String point1: Point point2 : Point point3: Point AnotherPoint1: AnotherPoint[] AnotherPoint2: AnotherPoint[] AnotherPoi ...

How to generate an interactive text-box using Angular 8 and connecting the input to the component during form submission

When I attempt to add a dynamic text box after clicking a button, the text box is successfully added. However, I am facing an issue where I am unable to retrieve all the values (values of dynamically added text boxes) when the form is submitted. It seems t ...

Using TypeScript to narrow down types within mapped types

Can you create a mapped type based on the property type? For example, if I want to map all properties with type String to Foo and all other types to Bar. Can this be done like this: type MappedType<T> = { [P in keyof T]: T[P] === String ? Foo : B ...

Is it possible for TypeScript to automatically determine the type of an imported module based on its path?

I'm currently working on creating a function, test.isolated(), which wraps around jest.isolateModules. This function takes an array of strings representing the modules to be imported, along with the usual arguments (name, fn, timeout), and then inject ...

Creating an object with mapped properties from enumeration values using Typescript

I am trying to find a way to automatically create an object with values computed during compilation using enumeration values and pre-defined function calls. The basic concept is to associate certain arguments of a function with keys. For example, consider ...

Having trouble with 'npm <script-command>' not working? Try changing it to 'npm run-script <script-command>' instead

Currently, I am configuring a node js backend to operate on TS for the first time within a mono-repo that has a specific folder structure. You can view the structure here. The package.json file is located in the main directory as shown below: "scr ...

Typescript interface created specifically for React Higher Order Component Props

Consider the React HOC provided below which adds sorting state to a component: import React, {Component, ComponentClass, ComponentType} from 'react' interface WithSortState { sortOrder: string } interface WithSortInjectedProps { sortO ...

The NGINX reverse proxy fails to forward requests to an Express application

I am currently in the process of setting up a dedicated API backend for a website that operates on /mypath, but I am encountering issues with NGINX not properly proxying requests. Below is the nginx configuration located within the sites-enabled directory ...

Removing a value from a hashmap using Typescript - what is the best way to do it?

After successfully implementing a hashmap in typescript following a helpful post, I am facing an issue with removing something from the hashmap. TypeScript hashmap/dictionary interface To add a key to the keys field of my abstract Input class's hash ...