Using TypeScript generics to constrain to either a number or a string

I am working on coding a react input component that accepts a defaultValue parameter of type string | number. This component has a state type matching the type of the received defaultValue;

This is my code:

type TypeName<T> = T extends number ? "number" : "string";

interface IInputProps<P extends string | number>{
    defaultValue: P,
    rule: (value: P)=>boolean,
    onChange: (value: P)=>void
}

interface IInputState<P extends string | number>{
    value: P,
    type: TypeName<P>
}

class Input<P extends string | number> extends Component<IInputProps<P>,IInputState<P>>{
    constructor(props:IInputProps<P>){
        super(props);
        this.state = {
            type: typeof props.defaultValue,
            value: this.props.defaultValue
        }

An error is occurring in the constructor section

ERROR in [at-loader] ./src/components/Input/index.tsx:21:4
    TS2322: Type '"string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"' is not assignable to type 'TypeName<P>'.
  Type '"string"' is not assignable to type 'TypeName<P>'.

Any suggestions on how to resolve this issue?

Answer №1

If you happen to be a step ahead of the compiler in this scenario, congratulations on your cleverness. While you grasp that no matter what P represents, the type of props.defaultValue will align with TypeName<P>, the compiler struggles to reach the same conclusion. It simply identifies that typeof props.defaultValue could only result in one of the string outputs from the typeof operator: "string", "number", or "boolean," among others. Even if it were to narrow it down to just "string" or "number," it lacks the sophistication to connect the dots between the value and P to declare it as TypeName<P> (at least in TypeScript 3.2).

The easiest approach is to acknowledge your supremacy over the compiler and assert that typeof props.defaultValue can indeed be handled as a value of type TypeName<P>. The code snippet below should compile without issues:

this.state = {
    type: typeof props.defaultValue as TypeName<P>, // assertion
    value: this.props.defaultValue
}

As of now, there doesn't seem to be a method to guide the compiler towards verifying TypeName<P> autonomously, so any attempt - such as implementing a type guard - would essentially function as an assertion.

In case you need to make this assertion frequently, consider encapsulating it within a function like the one shown below:

function getTypeName<T extends string | number>(t: T): TypeName<T> {
  return typeof t as TypeName<T>; // assertion
}

This way, you confine the assertion to a specific location and shield subsequent users of getTypeName() from confronting the issue directly:

this.state = {
    type: getTypeName(props.defaultValue), // no assertion here
    value: this.props.defaultValue
}

Hopefully, this sheds some light on the situation. Best of luck!

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 database migration encounters an issue: The module 'typeorm' cannot be located

When I run the following commands: ❯ node --version v16.19.0 ❯ yarn --version 3.5.0 I am attempting to launch this project: https://github.com/felipebelinassi/typescript-graphql-boilerplate However, when I execute: yarn db:migrate which runs the c ...

Ways to update a component upon becoming visible in Angular

Within my Angular 4.3.0 project, the header, left panel, and right panels are initially hidden on the login page until a successful login occurs. However, once they become visible, the data is not pre-loaded properly causing errors due to the ngOnInit meth ...

Prevent updating components when modifying state variables

Introduction I've developed a React component that consists of two nested components. One is a chart (created with react-charts) and the other is a basic input field. Initially, I have set the input field to be hidden, but it becomes visible when the ...

The exploration of child routes and modules

I'm currently working on a somewhat large project and I've decided to break it down into modules. However, I'm facing an issue with accessing the routes of admin.module.ts. In my app.module, I have imported the admin Module. imports: [ Br ...

Error 2322: Troubleshooting Typescript arrow functions overloads issues

Everything seems to be working well with the code below, except for an error that occurs with the resolve constant. const resolve: Resolve Type '(param: "case 1" | "case 2" | "case 3") => boolean | "string" | ...

Tips for quietly printing a PDF document in reactjs?

const pdfURL = "anotherurl.com/document.pdf"; const handleDirectPrint = (e: React.FormEvent) => { e.preventDefault(); const newWin: Window | null = window.open(pdfURL); if (newWin) { newWin.onload = () => ...

Access SCSS variable values in Angular HTML or TypeScript files

So, I've been looking into whether it's feasible to utilize the SCSS variable value within HTML or TS in Angular. For instance: Let's say I have a variable called $mdBreakpoint: 992px; stored inside the _variable.scss file. In my HTML cod ...

Vue 3 - Compelled to utilize any data type with computedRef

Recently, I've been diving into Vue/Typescript and encountered a puzzling error. The issue revolves around a class named UploadableFile: export class UploadableFile { file: File; dimensions: Ref; price: ComputedRef<number>; ... constr ...

Typescript is throwing a Mongoose error stating that the Schema has not been registered for the model

I've dedicated a lot of time to researching online, but I can't seem to figure out what's missing in this case. Any help would be greatly appreciated! Permission.ts (This is the Permission model file. It has references with the Module model ...

Identifying collisions or contact between HTML elements with Angular 4

I've encountered an issue that I could use some help with. I am implementing CSS animations to move p elements horizontally inside a div at varying speeds. My goal is for them to change direction once they come into contact with each other. Any sugges ...

Using TypeScript, pass an image as a prop in a Styled Component

I am facing an issue with the code below that is supposed to display the "NoBillsLaptopPNG.src" image on the screen, but for some reason, the image is not showing up. The images are being imported correctly, so I'm unsure why the image is not appeari ...

Only one choice for discriminated unions in react props

Looking to create a typescript type for react component props, specifically a basic button that can accept either an icon prop or a text prop, but not both. My initial attempt with a discriminated union didn't quite produce the desired outcome: inter ...

Using Mat-Error for Two Way Binding leads to frequent triggering of ngModelChange事件

I am working with a mat input field that has two-way data binding using ngModel, and I want to add validation using mat-error and formControl. <mat-form-field [formGroup]="myForm"> <input matInput formControlName="myFormName" autocomplete="off" ...

The proper approach for managing downloaded d.ts files from DefinitelyTyped after installation through npm

Visual Studio 2017 Enterprise ASP.NET MVC Application TypeScript 2.5 SDK Source control is in TFS I have opted to use Microsoft's built-in property editor instead of creating a custom tsconfig.config file: To streamline my workflow, I rely on Mad&ap ...

When a reaction function is triggered within a context, it will output four logs to the console and

My pokemon API application is encountering some issues. Firstly, when I attempt to fetch a pokemon, it continuously adds an infinite number of the same pokemon with just one request. Secondly, if I try to input something again, the application freezes enti ...

What is preventing me from retrieving a value from a member function or method within a TypeScript class instance?

I am facing an issue with the FileInfo class that implements the IFileInfo interface. This class has an instance member function ext and a function getExt(). Within my component, there is a private method named openTempFolder() which makes an HTTP call to ...

Exploring multiple states within an interval function in React Native

I'm struggling to find the right words for this question. I've encountered an issue where I need to constantly check and update a complex state object within an interval loop in my program. To simplify, let's say it consists of just a counte ...

Steps for eliminating the chat value from an array tab in React

tabs: [ '/home', '/about', '/chat' ]; <ResponsiveNav ppearance="subtle" justified removable moreText={<Icon icon="more" />} moreProps={{ noCar ...

The Event Typing System

I am currently in the process of setting up a typed event system and have encountered an issue that I need help with: enum Event { ItemCreated = "item-created", UserUpdated = "user-updated", } export interface Events { [Event.Ite ...

Unraveling the mysteries of an undefined entity

When the variable response is undefined, attempting to retrieve its property status will result in an error: Error: Unable to access property 'status' of undefined const { response, response: { status }, request, config, } = error as A ...