Enum-centric type guard

Could I create a custom type guard to verify if a specified string is part of a specific string enum in a more specialized way? Check out the following example:

enum MyEnum {
  Option1 = 'option one',
  Option2 = 'option two',
}

const checkIfMyEnum = (value: any): value is MyEnum => {
  return Object.values(MyEnum).includes(value as MyEnum);
};

Is there a method to convert this into a generic approach, allowing me to apply the same validation logic across various string enums?

Answer №1

Is this what you're asking for?

const checkEnum = <T>(e: T) => (value: any): value is T[keyof T] =>
    Object.values(e).includes(value as T[keyof T]);

Therefore, checkEnum generates type guard functions based on enum objects. The type T[keyof T] represents the types of property values in T.

const checkMyEnum = checkEnum(MyEnum);
// const checkMyEnum: (value: any) => value is MyEnum

By invoking checkEnum(MyEnum), the type T is automatically deduced as typeof MyEnum, and then T[keyof T] corresponds to the property values of that, which are essentially MyEnum.

I hope that clarifies things. Best of luck!

Link to code

Answer №2

When it comes to TypeScript string enums and number enums, their JavaScript emissions are quite different.

The solution provided works well for scenarios involving string enums.

However, individuals utilizing number enums might mistakenly assume that the same solution applies to their specific situation. It's important to proceed with caution in such cases.

//number enum here
enum E {
  A,
  B,
  C,
}

const isSomeEnum = <T>(e: T) => (token: any): token is T[keyof T] =>
  (Object as any).values(e).includes(token as T[keyof T]);

console.log(isSomeEnum(E)("A")); //expected false, actual true
console.log(isSomeEnum(E)(0));   //expected true , actual true

function isSomeEnum2<T> (e: T) : (token: unknown) => token is T[keyof T] {
  const keys = Object.keys(e)
    .filter((k) => {
      return !/^\d/.test(k);
    });
  const values = keys.map((k) => {
    return (e as any)[k];
  });
  return (token: unknown): token is T[keyof T] => {
    return values.includes(token);
  };
};

console.log(isSomeEnum2(E)("A")); //expected false, actual false
console.log(isSomeEnum2(E)(0));   //expected true , actual true

Playground

Answer №3

An alternative approach to @jcalz's solution is to create a simple function that can be easily utilized:

const checkEnumValue = <T extends { [k: string]: string }>(valueToCheck: any, enumType: T): valueToCheck is T[keyof T] =>
    typeof valueToCheck === 'string' && Object.values(enumType).includes(valueToCheck);

As noted by Justin AnyhowStep, this function specifically caters to string enums, hence the

T extends { [k: string]: string }
condition. Here's how it operates:

enum StringEnum {
    A = 'aaa',
    B = 'bbb',
}

enum NumberEnum {
    A,
    B,
}

let example;

if (checkEnumValue(example, StringEnum)) {
    if (example === 'SOMETHING') {
        // Compiler raises an error:
        // This condition will always return 'false' since the types 'StringEnum' and '"SOMETHING"' have no overlap.
    }
}
if (checkEnumValue(example, NumberEnum)) {
    // Compiler raises an error:
    // Argument of type 'typeof NumberEnum' is not assignable to parameter of type '{ [k: string]: string; }'.
    // Property 'A' is incompatible with index signature.
    // Type 'NumberEnum' is not assignable to type 'string'.
}

Answer №4

After searching through npm, I couldn't locate a package that fit my needs. That sparked the idea to develop my own: https://www.npmjs.com/package/is-some-enum. Hopefully, it can be of assistance to someone out there :)

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

Angular Typescript subscription value is null even though the template still receives the data

As a newcomer to Angular and Typescript, I've encountered a peculiar issue. When trying to populate a mat-table with values retrieved from a backend API, the data appears empty in my component but suddenly shows up when rendering the template. Here&a ...

Finding the perfect pairing: How to align counters with objects?

public counter = 0; x0: any; x1: any; x2: any; x3: any; x4: any; next(){ this.counter += 1; this.storage.set("Count", this.counter); console.log(this.counter); this.logic(); } logic(){ //automatic counter here var xNum = JSON.parse(JSON.stri ...

Understanding and processing HTML strings in typescript

I am currently utilizing TypeScript. Within my code, there is an object named "Reason" where all variables are defined as strings: value, display, dataType, and label. Reason = { value: '<ul><li>list item 1</li><li&g ...

Error: Unable to locate Angular2 Custom Service

I have implemented a custom service to populate a list of people in my HTML. Below is the code for my custom service: app.peopleListService.ts import { Injectable } from '@angular/core'; import { Person } from "../model/peopleModel"; @Injecta ...

Guide on retrieving an ArrayList() from intricate function in Angular

Simplicity is the key to my question. Let's take a look at this Angular method: getAllOrdersHeaders(){ this.getAllOrdersIds().subscribe(idList=>{ idList.forEach(id=>{ this.ordersCollection.doc(id).collection('metadata&apo ...

Build upon a class found in an AngularJS 2 library

Whenever I attempt to inherit from a class that is part of a library built on https://github.com/jhades/angular2-library-example For example, the class in the library: export class Stuff { foo: string = "BAR"; } And the class in my application: exp ...

Improving type definitions in Typescript 2.6 using module augmentation leads to error TS2339: Property '' is not found on type ''

Currently utilizing the material-ui library version v1.0.0-beta. An update was released yesterday to v1.0.0-beta.28, however, the type definitions were not updated resulting in runtime errors while compilation remains successful. Encountering this error i ...

Discover the Hassle-Free Approach to Triggering Angular Material Menu with ViewChild and MatMenuTrigger

Is there a way to programmatically open an Angular Material menu using a Template Reference Variable on a button trigger that is accessed in the component through ViewChild? I want the menu to open when the mouse hovers over it, instead of just clicking i ...

When using TypeScript with custom components as children in React, the `type` returned by React.Children is a string representing the function

It might sound a bit odd, or maybe I'm completely off track here. While going through some articles and React documentation on getting children and identifying specific child components using React.Component.map(), I ran into an issue with my custom c ...

Using TypeScript to specify data types in the Vue data object

I am currently utilizing Vue.js with Typescript in a webpack project. Following the guidelines provided in the Recommended Configuration in my tsconfig.json, I have set: "strict": true, Within one of my components, I have: declare interface P ...

Observable subscription results in a return of undefined

My observable is being filled with data from the backend using a service. The backend is providing the correct data, but I am having trouble building a pie chart with the values from the observable. The relevant part of the code is as follows: this.dataSe ...

Should an HTML canvas in Angular be classified as a Component or a Service?

I have a basic drawing application that uses an MVC framework in TypeScript, and I am looking to migrate it to Angular. The current setup includes a Model for data handling, a View for rendering shapes on the canvas, and a Controller to manage interactio ...

Material UI React Autocomplete Component

I'm currently working on integrating an Autocomplete component using the Material UI library. However, I've encountered a challenge - I'm unsure of how to properly pass the value and onChange functions, especially since I have a custom Text ...

Retrieving child elements from parent identifiers using Typescript

I've been working on creating a new array with children from the data fetched from my database. While my initial attempt was somewhat successful, I believe there are some missing pieces. Could you assist me with this? Here is the raw data retrieved f ...

Splitting the string into individual characters using string.split("").map may cause layout issues on small devices

Shoutout to @pratik-wadekar for the amazing help in creating a working text animation. However, I encountered an issue when testing it on various screen sizes and mobile devices - the animated word plants breaks into pieces like PLA on one line and NTS on ...

How can I call a global function in Angular 8?

Currently implementing Angular 8, my objective is to utilize downloaded SVG icons through a .js library. To achieve this, I have made the necessary additions to my .angular.json file: "scripts": [ "node_modules/csspatternlibrary3/js/site ...

Limit the elements in an array within a specified range of dates

Currently, I am working on implementing a filter functionality for a data array used in a LineChart within my Angular application using TypeScript. The structure of the data array is as follows: var multi = [ { "name": "test1", "series": [ ...

Prompt user to save changes or cancel before closing modal (if closed by pressing ESC or clicking the backdrop)

When I manually close the modal, everything works fine. I just create a prompt and only call the BsModalRef.hide() method when the prompt (sweetalert) is closed. However, when the modal is closed using the ESC key or click-outside events provided by Boots ...

Using Angular to include a forward slash "/" in the text input for a date field

Hello everyone, I am a newcomer to AngularJS and I am looking to insert slashes in an input type text element. I prefer not to rely on external packages like angular-ui or input type Date. My goal is to have the format mm/dd/yyyy automatically applied as ...

Utilize string variables within TypeScript's enumeration feature

Can string variables be used in enums in TypeScript? Strings can be used in enum like so: enum AllDirections { TOP = 'top', BOTTOM = 'bottom', LEFT = 'left', RIGHT = 'right', } However, trying to use variab ...