Tips for type guarding in TypeScript when using instanceof, which only works with classes

Looking for a way to type guard with TypeScript types without using instanceof:

type Letter = 'A' | 'B';
const isLetter = (c: any): c is Letter => c instanceof Letter; // Error: 'Letter' only refers to a type, but is being used as a value here.

// Desired functionality: Type guard filtering.
isLetter('a'); // Should return true
'foo bar'.split('').filter(c => isLetter(c)); // Should output 'a'

No luck in finding similar issues where instanceof works differently when used with classes:

class Car {}
const isCar = (c: any): c is Car => c instanceof Car; // No error
isCar('a'); // returns false

If it seems like instanceof only functions with classes, what alternative could be considered for types and how can we effectively type guard with a TypeScript type?

Answer №1

Throughout the compilation phase, TS types are present but disappear during runtime.

When creating a user-defined type guard, it is important to establish appropriate checks based on the input and output types. It's easier to choose from a few options rather than assert the structure of an entirely unfamiliar object.

For a type like Letter (a union of 'A' and 'B'), simply checking whether the input is A or B will suffice

const isLetter = (c: any): c is Letter => c == 'A' || c == 'B';

If your union contains more elements and you prefer not to repeat them in both the union and the type guard:

const letters = ['A', 'B'] as const;
type Letter = typeof letters[number];
const isLetter = (c: any): c is Letter => letters.includes(c);

Playground link

Note: Classes persist at runtime through the prototype chain, allowing the use of the instanceof operator with classes.

Answer №2

One potential approach to type guarding a union of string types is to compare the input against a predefined list of strings:

const isValidString = (str: any): str is ValidString => ['apple', 'banana'].includes(str);
isValidString('apple'); // true
isValidString('cherry'); // false

This solution is not optimal as it includes duplication of the union strings within both the type and the guard function. Alternative suggestions are welcome!

Answer №3

Typescript operates at compile-time, introducing concepts that vanish once the code is running in the JavaScript runtime environment. Attempting to use a Typescript type for runtime checks within your functions will not be effective.

Instead of implementing a runtime check using Typescript, focus on substituting the any parameter type with a specific TypeScript type like Letter, which allows for compile-time validation.

The following example demonstrates this approach:

type Letter = 'A' | 'B';
const shouldBeLetter = (c: Letter) => {
  if (c !== 'A' && c !== 'B') throw "c should be either 'A' or 'B'";
  /* rest of the function */
}

shouldBeLetter('C'); // compile-time error
let couldBeC : Letter = 'ABC'[Math.floor(Math.random() * 3)] as Letter;
shouldBeLetter(couldBeC); // requires a runtime test

The distinction between the expression context and type context in TypeScript highlights how compile-time types are separate from the runtime execution environment. This separation allows for unique functionalities such as repurposing JavaScript keywords like typeof in the TypeScript type context.

Additionally, utilizing expressions like letters[number] in the type context can extract type information from variables, enhancing type safety in your code.

@Lesiak's response effectively demonstrates the utilization of these TypeScript features by leveraging enumerated-type arrays across different contexts in the code.

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

When working with Typescript, you can declare an interface and split its definition across multiple files

I've been developing a software application that utilizes WebSocket with the NodeJS ws package. My networking structure revolves around a module responsible for handling message reception and transmission. Given that I'm working with TypeScript, ...

When using React MUI Autocomplete, make sure to handle the error that occurs when trying to filter options using the

I am trying to implement an autocomplete search bar that makes a custom call to the backend to search through a list of tickers. <Autocomplete multiple id="checkboxes-tags-demo" options={watchlis ...

Is there a way to integrate the AuthState TypeScript Interface into the react-oidc-context Node package for testing in Next.js?

We are currently working on a Next.js application that utilizes the react-oidc-context Node module for authentication with ADFS. During our testing phase using Vitest, we encountered the following error: TypeError: Cannot read properties of undefined (rea ...

Comparing Angular global variables: when to use static readonly in service class versus const

Which method is the better choice? @Injectable({ providedIn: 'root' }) export class MyService { static readonly VALIDITIES = new Map<number, string>([ ... ]); ... } OR: const Validities = new Map<number, string>([ .. ...

Issue encountered when working with interface and Observable during the http parsing process

Visual Studio File Structure Error app(folder) -->employee-list(folder) -->employee-list.component.html -->employee-list.component.ts -->app.component.html -->app.component.ts -->app.module.ts -->employee.json ...

Checking React props in WebStorm using type definitions

Currently, I am utilizing WebStorm 2018.3.4 and attempting to discover how to conduct type checking on the props of a React component. Specifically, when a prop is designated as a string but is given a number, I would like WebStorm to display an error. To ...

What is the best way to limit the types of function parameters in TypeScript based on whether the parameter index is even or odd?

My goal is to create a function with an unlimited number of parameters, where the type of each parameter is determined by whether its index is odd or even. For example: flow(isMachineReady(), 'and', isWaterHot(), 'or', isMilkHot(), &ap ...

Is there a way to clear the selected date in a date picker while using MatDateRangeSelectionStrategy?

Recently, I was experimenting with the date picker feature of Angular Material and stumbled upon this particular example After implementing this example with my own logic, everything seemed to be working perfectly fine except for one issue. The dates were ...

Invoke a function of a child component that resides within the <ng-content> tag of its parent component

Check out the Plunkr to see what I'm working on. I have a dynamic tab control where each tab contains a component that extends from a 'Delay-load' component. The goal is for the user to click on a tab and then trigger the 'loadData&apo ...

Acessing files from Azure Blob within the Aurelia UI: Download or View now!

I currently have my files stored in Azure and I am looking for a way to either download or view them on the client side. This is how I envision the process: Azure -> Api -> Client UI (Aurelia) While I have come across several C# examples, I am unsure of ...

Utilizing movingMarker from leaflet-moving-marker in Angular: A Step-by-Step Guide

I am currently working on incorporating the leaflet-moving-marker plugin but encountering some errors in the process. import {movingMarker} from 'leaflet-moving-marker' var myMovingMarker = L.movingMarker([[48.8567, 2.3508],[50.45, 30.523 ...

The PKIJS digital signature does not align with the verification process

Explore the code snippet below const data = await Deno.readFile("./README.md"); const certificate = (await loadPEM("./playground/domain.pem"))[0] as Certificate; const privateKey = (await loadPEM("./playground/domain-pk ...

After I deploy my Next.js code to Vercel, including Google Analytics added by @next/third-parties, I am encountering an error that does not appear in development mode

Lately, I completed a next.js project and integrated Google Analytics using @next/third-parties/google. During development, everything worked perfectly, but upon deploying it to vercel.com, an error popped up. ` ./app/layout.tsx:3 ...

Resolving issues with Typescript declarations for React Component

Currently utilizing React 16.4.1 and Typescript 2.9.2, I am attempting to use the reaptcha library from here. The library is imported like so: import * as Reaptcha from 'reaptcha'; Since there are no type definitions provided, building results ...

Why does mapping only give me the last item when I try to map onto an object?

Why does mapping onto an object only give me the last item? Below is the object displayed in the console: 0: {Transport: 2} 1: {Implementation: 9} 2: {Management: 3} When I use ngFor, it only provides the last item const obj = this.assigned_group; // r ...

What are the steps to create a dynamic navigation menu in Angular 2?

I have successfully implemented this design using vanilla CSS and JS, but I am encountering challenges when trying to replicate it in Angular 2. Setting aside routing concerns, here is the current state of my component: navbar.component.ts import { Comp ...

Triggering ngSubmit function when button is clicked inside an Ionic alert

My ionic app is up and running, utilizing a template driven form in Angular to gather user input. I'm using ngSubmit to pass this data to my ts.file. My challenge lies in triggering the ngSubmit function through a 'No and save data' button w ...

Angular Update Component on Input ChangeEnsuring that the component is automatically

<div class=" card-body"> <div class="row"> <div class=" font-icon-list col-lg-2 col-md-3 col-sm-4 col-xs-6 col-xs-6" routerLinkActive="active" *ngFor="let subject of subjects"> <div class=" fon ...

Looking to compare two elements within different arrays? The method outlined below is specifically designed for comparing to individual values rather than entire arrays

Is this the right approach? How can we iterate through each array to compare values? Should these data structures be modified or transformed first? Below is the data that needs to be compared. The objective is to match userID with DocumentID. const videos ...

The closeOnClickOutside feature seems to be malfunctioning in the angular-2-dropdown-multiselect plugin

I'm currently using 2 angular-2-dropdown-multiselect dropdowns within a bootstarp mega div. The issue I'm facing is that when I click on the dropdown, it opens fine. However, when I click outside of the dropdown, it doesn't close as expected ...