Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to properly filter by enum value in Typescript?

If I have an enum defined like this:

export enum State {
    Archived = 0,
    OnShoppingListChecked = 1,
    OnShoppingListUnchecked = 2
}

and a class that uses the enum:

import { State } from "./State";
export class GroceryDto {
  public name: string = null;
  public currentState: State = null;
}
export default GroceryDto;

I have some code that is trying to filter based on the currentState of GroceryDto, but is not qualifying anything to be entered into the array.

//...some code to fetch data
let activeGroceries = response.data.activeGroceries as GroceryDto[]; //casts correctly to an array of 4 items
let visibleGroceries = activeGroceries.filter(
  g => g.currentState === State.OnShoppingListUnchecked
);
//visibleGroceries is empty but should return the same 4 values

I was able to get the result I wanted by casting to a string, but that seems like it should be unnecessary if I'm using typescript.

let filtered = visibleGroceries.filter(
  g =>
    g.currentState.toString() === State[State.OnShoppingListUnchecked]
);
like image 740
colinwurtz Avatar asked Mar 21 '26 12:03

colinwurtz


2 Answers

Seems the discrepancy was in how the server was sending back the data, currentState on each GroceryDto was being sent over as a string. Changing the enum definition like @Aleksey mentioned solved my problem:

export enum State {            
        Archived = "Archived",
        OnShoppingListChecked = "OnShoppingListChecked",
        OnShoppingListUnchecked = "OnShoppingListUnchecked"
    }
like image 169
colinwurtz Avatar answered Mar 24 '26 02:03

colinwurtz


Be advised, that enuma in TypeScript are quite... well they might be misunderstood, and always might be used as string-based and numeric-based type.

If you use your enum in form like you presented:

State.OnShoppingListUnchecked

Then it will be represented as number and compared against numeric value of 1. But consider following cross-matching enum code:

a === State['OnShoppingListUnchecked'] // compare against 1
a === State[State['OnShoppingListUnchecked']] // compare against 'OnShoppingListUnchecked' string
a === State[State[State['OnShoppingListUnchecked']]] // Again against numeric 1
// ... and so one 

Probably you are getting from server side string, not a number, and thus your checks are failed. You can add a bit of automation to that checking in following way:

let toCompare = 1 // Field you wish to compare against
if(typeof(compareValue) === 'number') {
  return State[toCompare ] === compareValue // Compare number against number
} else if (typeof(compareValue) === 'string') {
  return State[State[toCompare ] === compareValue // compare input string against string representation of enum
}

To ensure, regardless of is input string or number, you will be always comparing against correct enum entry.

And keep in mind, string-based enums are supported since TyeScript 2.4 and upper, and in my personal opinion usage of them shall be minimized, mostly because number-based enums are convenient way to choose some additional information from array-arranged structures, while string-based are impossible to use this way:

enum NamesEnum {
    NAME0 = 0,
    NAME1 = 1
};

namesToDisplay: string[] = [
  'Name1 display value',
  'Name2 display value'
];

let enumVal = someFunctionThatGetsEnum(); // Works only for number-based enums
this.currentDisplay = this.namesToDisplay[enumVal];

and in template:

<p>{{ currentDisplay  }} </p>

If you don't have use-case scenario for array indexing, then string-based enums can be replaced with String Literal Types with same benefits as enums gives you but with less worrying about type and compatibility issues.

like image 24
Tomas Avatar answered Mar 24 '26 01:03

Tomas



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!