Uncertainty surrounding refinement in Typescript

I've been diving into the book Programming TypeScript, and I'm currently stuck on understanding the concept of refinement as shown in this example:

type UserTextEvent = { value: string; target: HTMLInputElement };
type UserMouseEvent = { value: [number, number]; target: HTMLElement };

type UserEvent = UserTextEvent | UserMouseEvent;

function handle(event: UserEvent) {
  if (typeof event.value === "string") {
    event.value; // string
    event.target; // HTMLInputElement | HTMLElement (1)
    // ...
    return;
  }
  event.value; // [number, number]
  event.target; // HTMLInputElement | HTMLElement (2)
}

In my interpretation, the type of event.target should be HTMLInputElement for (1) and HTMLElement for (2). I believe that if the type of event.value is specific, then the type of event.target should also be specific. However, it appears to be HTMLInputElement | HTMLElement instead. Can someone provide a clearer explanation than what's given in the book? Your help is much appreciated.

Answer №1

By only checking the type of the property, TypeScript will not automatically assume the class type. To achieve this, you must use a method to infer the type yourself.

type UserTextEvent = { value: string; target: HTMLInputElement };
type UserMouseEvent = { value: [number, number]; target: HTMLElement };

type UserEvent = UserTextEvent | UserMouseEvent;

function isUserTextEvent(event: UserEvent): event is UserTextEvent {
  return typeof event.value === "string";
}

function handle(event: UserEvent) {
  if (isUserTextEvent(event)) {
    event.value; // string
    event.target; // HTMLInputElement  (1)
    // ...
    return;
  }
  event.value; // [number, number]
  event.target; // HTMLElement (2)
}

You can test this in the playground

when you verify

typeof event.value === "string"

TypeScript will recognize that the value is indeed a string, and else it can only be a [number, number].

However, it cannot automatically infer that value is of type HTMLInputElement or HTMLElement, so it still considers it as HTMLInputElement | HTMLElement. You need to specify with a method that the event is actually of type UserTextEvent.

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

Leveraging TypeScript for defining intricate tree manipulation guidelines

In my current project, I am working on enhancing a TypeScript process that is in place. The goal is to make it more strongly typed for better scalability and accuracy. The structure of the existing tree under consideration is as follows: interface Node { ...

Displaying personalized message 'Loading...' while waiting for the data to be retrieved from the API

How can I display a 'Loading' text message until the data is fetched from the API? Currently, it just shows a message saying 'No data to display' I have searched through the documentation but couldn't find any related information. ...

Retrieve information from various MongoDB collections

Greetings! I currently have a database with the following collections: db={ "category": [ { "_id": 1, "item": "Cat A", }, { "_id": 2, "item": "Cat B" ...

Tips for effectively highlighting search text within HTML content without causing any issues

I am in search of a solution that can assist me in searching for specific terms within an HTML string while also highlighting them. I have the ability to remove the HTML content from the string, but this poses the problem of losing the context of the origi ...

Transfer text between Angular components

Here is the landing-HTML page that I have: <div class="container"> <div> <mat-radio-group class="selected-type" [(ngModel)]="selectedType" (change)="radioChange()"> <p class="question">Which movie report would you like ...

Error: The template is unable to parse due to unrecognized element 'mat-label'

When attempting to write about the datepicker, I encountered the following error: Uncaught Error: Template parse errors: 'mat-label' is not a recognized element: If 'mat-label' is an Angular component, please ensure that it is part o ...

What causes inability for JavaScript to access a property?

My current coding project involves the usage of typescript decorators in the following way: function logParameter(target: any, key : string, index : number) { var metadataKey = `__log_${key}_parameters`; console.log(target); console.log(metadataKey ...

Retrieve the predetermined value from the dropdown menu option

I currently have two classes with a mapping structure as follows: User *--------------------1 Sexe Users are listed in the file list-users.component.html. When selecting a user for modification, I am redirected to the modify-user.component.html B ...

A feature that retrieves specific attributes from a type based on user-defined criteria

I am dealing with a group of types/interfaces that share common properties, but some cases do not utilize all of these properties. I would like to develop a function where only the names of the properties are passed to it. Here are the shared properties: ...

Is there a way to access a specific tab index in Ionic 3.20 from a child page using a function call?

Imagine having a tabs page with 3 index pages. The first index page is the home page, the second is the products page, and the third is the cart page. When navigating from the home page to the search page, there is a button that you want to click in orde ...

Angular 6 component experiencing issues with animation functionality

I've implemented a Notification feature using a Notification component that displays notifications at the top of the screen. The goal is to make these notifications fade in and out smoothly. In my NotificationService, there's an array that holds ...

Volta alert: Temporary directory creation failed

Recently, I attempted to globally download and install TypeScript using the npm install -g typescript command in my terminal. Unfortunately, an error occurred: Volta error: Could not create temporary directory in /Users/username/.volta/tmp/image/packages ...

When using Angular, a service can be declared in the viewProviders of the parent component and then accessed in the viewProviders of the child

Imagine a scenario where there is a parent component called AppComponent, a directive named MyDirective, and a service named SimpleService. In this case, MyDirective is utilized in the template of AppComponent and injects SimpleService using the Host deco ...

Exploring the depths of useDispatch and dispatch in React-Redux

I am currently analyzing the code written by a former colleague of mine. Based on my understanding, useDispatch accepts an object containing the action type and payload, which is then compared to all reducers to update the state accordingly. However, in t ...

What is the reason behind TypeScript rejecting the syntax of checkbox input elements?

When trying to use the following checkbox in TypeScript, I encountered a warning: <input type="checkbox" onClick={(event: React.MouseEvent<HTMLInputElement>) => { setIsTOSAccepted(event.target.checked); }} defaultChecked={ ...

Display Nested Data in Angular Material Table: A Step-by-Step Guide

I recently received a REST API response from the following URL: { "list": [ { "id": 1, "login": "<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="7c08190f08234d3c0508521f1311"& ...

Guide on making a Mapped Type in TypeScript using dynamic generic parameters

I am working with an object that contains one or more PreparedStatement. I am looking to create a general type definition for this object. Is there a way to achieve this in code? type PreparedRequest<Input, Result> = { input: Input; result: Resul ...

Leveraging functionality from an imported module - NestJS

Currently, I am utilizing a service from a services module within the main scaffolded app controller in NestJS. Although it is functioning as expected - with helloWorldsService.message displaying the appropriate greeting in the @Get method - I can't ...

Need at least one of two methods, or both, in an abstract class

Consider the following scenario: export abstract class AbstractButton { // Must always provide this method abstract someRequiredMethod(): void; // The successor must implement one of these (or both) abstract setInnerText?(): void; abst ...

Having difficulty constructing a full stack application using Express

I've been struggling to configure a full stack app using create-react-app with Express and TypeScript. My main issue is figuring out how to compile the server files into a build folder. I have separate tsconfig files for the server and create-react-ap ...