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

What sets apart .to and .toService in InversifyJS?

I find the documentation on using .toService(MyClass) for transitive bindings confusing. The examples provided also show that achieving the same result is possible with a regular .to(MyClass). https://github.com/inversify/InversifyJS/blob/master/wiki/tran ...

Webpack is having trouble identifying Node's process module

It has been more than ten years since I last worked with JavaScript, but recently I had an idea for an app that would be best implemented as a NodeJS app. As I delved into the modern JS ecosystem, like many others, I found myself thoroughly confused, haha. ...

Are there any methods for utilizing the Angular/flex-layout API within a TypeScript file in an Angular 11 project?

When working with Angular Material, the Angular Flex Layout proves to be quite beneficial. Is there a way to access the flex layout API within a TypeScript file? For instance, can we retrieve MediaQueries values from this link in a TypeScript file? breakp ...

Having trouble uploading an image using Angular, encountering an error in the process

Whenever I try to upload an image, the server keeps throwing an error saying Cannot read property 'buffer' of undefined. I am using Node.js as a backend server and interestingly, when I send the image through Postman, it gets stored in MongoDB wi ...

What steps can I take to ensure that AstroJS components do not conceal SVG elements when the SVG is incorporated into another file with client:load?

Currently, I am developing a weather application using Astro.js in conjunction with React. One of the features includes an SVG component that serves as the project logo and is implemented in the initial page loader. Upon the page loading, the SVG functions ...

What is the best way to showcase images at random in Angular?

I am trying to display a random array of images in the UI, but I'm encountering an error with innerHTML when using the code below in TypeScript. randomPic(){ this.randomNum= Math.floor(Math.random() * this.myPix.length); console.log(this.rando ...

PlayWright - Extracting the text from the <dd> element within a <div> container

Here is the structure I am working with: <div class="aClassName"> <dl> <dt>Employee Name</dt> <dd data-testid="employee1">Sam</dd> </dl> </div> I am attempting to retrie ...

Combining various POST requests by matching the common value in each array. (Angular)

Here are the two different sets of data: "statusCode": 200, "data": [ { "color": { "id": "1111", "name": null, "hex&quo ...

The 'component' property is not found in the 'IntrinsicAttributes' type in this context

I am facing an issue with a component that is not compiling properly: export default function MobileNav({routes, currentRouteIndex, handlePressedRoutedIndex}: MobileNavProp) { ... return ( <React.Fragment> ... ...

Sluggish website loading time

Hey there, I'm currently developing a website and I'm facing a major issue with one of my pages loading slowly and experiencing lag. I'm unsure if this is due to the on scroll listeners or the excessive references in my code. Could it possib ...

Identifying row expansion in ngx-datatable: detecting expand status on row click

Is there a way to determine if a line has already been expanded when using the toggle feature? When you click on a line, it expands and shows the details. Here is some code in HTML: <ngx-datatable #dataTable ... (select)='onRowSelect($eve ...

Similar to `util.inspect` in Node.js, Deno also has a function

Is there a utility function in Deno that can stringify an Object or primitive similar to Node.js util.inspect? For instance, if I have a JSON object in Node.js and want to display its contents: > m = {k1:'v1', k2:'v2'} { k1: ' ...

The challenge of handling Set type in TypeScript errors

I'm currently facing two errors while trying to convert a function to TypeScript. The issue lies with the parameters, which are of type Set import type {Set} from 'typescript' function union<T>(setA: Set<T>, setB: Set<T>) ...

Issues encountered when trying to upload images to Firestore Storage

I am attempting to upload an image and store its URL in a Firestore document. To achieve this, I have the following code snippet: This function uses the device camera to capture the photo. selectImage(): Promise<any> { return new Promise(resolv ...

Have you considered utilizing encodeURIComponent to encode both the key and parameter values?

When I use encodeURIComponent in this code snippet: export function getDownloadFileUrl(fid: string,bgColor: string) { const params = encodeURIComponent("id=" + Number(fid) + "&bgColor=" + bgColor); const config = { m ...

Efficiently transforming a nested object array into a single dynamic array

// I have a collection of various objects _id: "5e5d00337c5e6a0444d00304" orderID: 10355 orderDate: "2020-03-02" user: _id: "5e2e9699a648c53154f41025" name: "xyz1" email: "<a href="/cdn-cgi/l/email-protection" class="_ ...

Create an eye-catching hexagon shape in CSS/SCSS with rounded corners, a transparent backdrop, and a

I've been working on recreating a design using HTML, CSS/SCSS in Angular. The design can be viewed here: NFT Landing Page Design Here is a snippet of the code I have implemented so far (Typescript, SCSS, HTML): [Code here] [CSS styles here] [H ...

Retrieve the key values from an object of a generic type

Is there a way to retrieve the keys of the object when it is of type T? I attempted to accomplish this using different methods such as: function getGenericTypeKeys<T>(): string[] { return Object.keys({} as T); } and function getGenericTypeKeys< ...

Dynamically pass a template to a child component

How can I dynamically load content on my page based on the active navigation point? export class Sub_navigation_item { constructor( public title: string, public templateName: string ) {} } I have a navigation item with an ID from an ...

Setting options using the form group in dropdowns in Angular can greatly enhance the user experience

I have created a FormGroup called holidayform and set it up as follows: holidayform: FormGroup; this.holidayform = this.fb.group({ title: ['', [Validators.required]], entryDate: ['',], }) this.holidayform.patchValue ...