Question

How to Check if a Value Exists in an Enum-Like Object in TypeScript

typescriptjavascriptenums

Question

I receive a numeric value such as 3 and need to check whether it exists in this TypeScript constant object:

export const MESSAGE_TYPE = {
  INFO: 1,
  SUCCESS: 2,
  WARNING: 3,
  ERROR: 4,
};

One approach I found is to convert the object values into an array and then use indexOf, for example:

if (-1 < _.values(MESSAGE_TYPE).indexOf(_.toInteger(type))) {
  // do stuff...
}

Is there a simpler and more readable way to check whether a value exists in this enum-like object?

Short Answer

By the end of this page, you will understand how to check whether a value exists inside an enum-like object in TypeScript. You will learn readable approaches using Object.values(), how this differs from checking keys, when to use a real enum, and how to avoid common mistakes with string and number comparisons.

Concept

In this question, the main concept is checking whether a value exists in a fixed set of allowed constants.

Your MESSAGE_TYPE example looks like an enum, but technically it is a plain object used as an enum-like constant map:

export const MESSAGE_TYPE = {
  INFO: 1,
  SUCCESS: 2,
  WARNING: 3,
  ERROR: 4,
};

This is common in TypeScript and JavaScript projects. Developers often use objects like this to define a small set of valid values.

The goal is simple:

  • you receive some input such as 3
  • you want to know whether that number is one of the allowed values

A readable way to do this is:

Object.values(MESSAGE_TYPE).includes(type)

If type might not already be a number, convert it first:

Object.values(MESSAGE_TYPE).includes(Number(type))

Why this matters in real programming:

  • validating API input
  • checking configuration values
  • deciding whether to show a message, status, or alert
  • preventing invalid data from reaching business logic

This is really a membership check: “Is this value one of the allowed values?”

In TypeScript, there are two related but different ideas:

  • checking keys: does "WARNING" exist?
  • checking values: does 3 exist?

Those are not the same, and many beginners mix them up. In your case, you want to check the values.

Mental Model

Think of MESSAGE_TYPE like a small menu:

  • INFO → 1
  • SUCCESS → 2
  • WARNING → 3
  • ERROR → 4

If someone gives you the number 3, you are not asking:

  • “Is there a menu item named 3?”

You are asking:

  • “Is 3 one of the codes on the menu?”

So the job is to scan the list of allowed codes and see whether the number appears there.

A simple mental model:

  • keys are the labels on the boxes
  • values are the contents inside the boxes

Your question is about checking the contents, not the labels.

Take Quiz

Syntax and Examples

The most readable modern approach is to use Object.values() with includes().

Basic syntax

Object.values(MESSAGE_TYPE).includes(value)

Example

export const MESSAGE_TYPE = {
  INFO: 1,
  SUCCESS: 2,
  WARNING: 3,
  ERROR: 4,
};

const type = 3;

if (Object.values(MESSAGE_TYPE).includes(type)) {
  console.log("Valid message type");
}

Why this is clearer

  • Object.values(MESSAGE_TYPE) gives [1, 2, 3, 4]
  • .includes(type) checks whether type is in that array
  • the code reads almost like plain English

If the input might be a string

Sometimes values come from forms, URLs, or APIs as strings:

const typeFromApi = "3";

if (Object.values(MESSAGE_TYPE).includes(Number(typeFromApi))) {
  console.log("Valid message type");
}

With a helper function

If you do this often, wrap it in a reusable function:

export const MESSAGE_TYPE = {
  INFO: 1,
  SUCCESS: 2,
  WARNING: 3,
  ERROR: 4,
} as const;

function isValidMessageType(value: unknown): boolean {
  return Object.values(MESSAGE_TYPE).includes(Number(value) as typeof MESSAGE_TYPE[keyof typeof MESSAGE_TYPE]);
}

console.log(isValidMessageType(3));    // true
console.log(isValidMessageType(9));    // false
console.log(isValidMessageType("2")); // true

This makes the rest of your code easier to read:

if (isValidMessageType(type)) {
  // do stuff...
}

Step by Step Execution

Consider this example:

const MESSAGE_TYPE = {
  INFO: 1,
  SUCCESS: 2,
  WARNING: 3,
  ERROR: 4,
} as const;

const type = 3;
const values = Object.values(MESSAGE_TYPE);
const exists = values.includes(type);

console.log(values);
console.log(exists);

Here is what happens step by step:

  1. MESSAGE_TYPE is created as an object:

    • INFO has value 1
    • SUCCESS has value 2
    • WARNING has value 3
    • ERROR has value 4
  2. type is set to 3.

  3. Object.values(MESSAGE_TYPE) creates an array of the object's values:

[1, 2, 3, 4]
  1. values.includes(type) checks whether 3 is inside [1, 2, 3, 4].

  2. Since 3 is present, exists becomes true.

  3. The output is:

[1, 2, 3, 4]
true

Now try another case:

const type = 7;
const exists = Object.values(MESSAGE_TYPE).includes(type);
console.log(exists);

Step by step:

  • values are still [1, 2, 3, 4]
  • includes(7) checks for 7
  • 7 is not found
  • result is false

Real World Use Cases

Checking whether a value exists in an enum-like object is very common.

API input validation

An API may receive a message type code:

const inputType = Number(req.body.type);

if (!Object.values(MESSAGE_TYPE).includes(inputType)) {
  throw new Error("Invalid message type");
}

Form processing

A dropdown may send numeric values as strings:

const selectedType = Number(formData.type);

if (Object.values(MESSAGE_TYPE).includes(selectedType)) {
  saveMessageType(selectedType);
}

Configuration checks

A config file may define a status level or logging level:

const level = Number(config.messageType);

if (!Object.values(MESSAGE_TYPE).includes(level)) {
  console.warn("Unsupported message type in config");
}

Defensive programming

When data comes from outside your code, never assume it is valid. Membership checks help keep invalid values out of the rest of the application.

Take Quiz

Real Codebase Usage

In real projects, developers usually do not inline this logic everywhere. They wrap it in validation helpers and use patterns that keep the code readable.

Common pattern: validation helper

const MESSAGE_TYPE = {
  INFO: 1,
  SUCCESS: 2,
  WARNING: 3,
  ERROR: 4,
} as const;

function isMessageType(value: unknown): value is typeof MESSAGE_TYPE[keyof typeof MESSAGE_TYPE] {
  return Object.values(MESSAGE_TYPE).includes(Number(value) as typeof MESSAGE_TYPE[keyof typeof MESSAGE_TYPE]);
}

This keeps calling code clean:

if (!isMessageType(type)) {
  return;
}

Guard clauses

A very common style is to reject invalid input early:

function handleMessage(type: unknown) {
  if (!Object.values(MESSAGE_TYPE).includes(Number(type))) {
    throw new Error("Invalid message type");
  }

  // safe to continue
}

Validation before branching

Developers often validate first, then switch or map based on the value:

function getLabel(type: number): string {
  if (!Object.values(MESSAGE_TYPE).includes(type)) {
    return "Unknown";
  }

  switch (type) {
    case MESSAGE_TYPE.INFO:
      return "Info";
    case MESSAGE_TYPE.SUCCESS:
      return "Success";
    case MESSAGE_TYPE.WARNING:
      return "Warning";
    case MESSAGE_TYPE.ERROR:
      return "Error";
    default:
      return "Unknown";
  }
}

Precomputed sets for frequent checks

If checks happen many times, a Set can make intent even clearer:

const messageTypeValues = new Set(Object.values(MESSAGE_TYPE));

function isMessageType(value: number): boolean {
  return messageTypeValues.has(value);
}

This is useful when membership checks are repeated often.

Common Mistakes

1. Checking keys instead of values

This checks property names like "INFO", not numeric values like 1 or 3:

const type = 3;

if (type in MESSAGE_TYPE) {
  console.log("exists");
}

Why it is wrong:

  • in checks whether a property name exists in the object
  • the keys are INFO, SUCCESS, WARNING, ERROR
  • 3 is a value, not a key

2. Forgetting type conversion

const type = "3";

console.log(Object.values(MESSAGE_TYPE).includes(type));

This returns false because:

  • array values are numbers
  • type is a string
  • includes() uses strict comparison

Fix:

console.log(Object.values(MESSAGE_TYPE).includes(Number(type)));

3. Using indexOf when includes is clearer

This works, but is less readable:

if (Object.values(MESSAGE_TYPE).indexOf(type) !== -1) {
  // valid
}

Prefer:

if (Object.values(MESSAGE_TYPE).includes(type)) {
  // valid
}

4. Assuming any number is valid because the type is number

TypeScript's number type is broad. It does not mean “one of the allowed values”.

let type: number = 999;

This is still a valid number, but not a valid message type. You still need runtime validation.

5. Not using as const when you want stricter typing

Without as const, TypeScript may widen the values to just number.

const MESSAGE_TYPE = {
  INFO: 1,
  SUCCESS: 2,
  WARNING: 3,
  ERROR: 4,
};

Better:

const MESSAGE_TYPE = {
  INFO: 1,
  SUCCESS: 2,
  WARNING: 3,
  ERROR: 4,
} as const;

This helps TypeScript preserve the exact literal values.

Comparisons

Here are the common ways to check membership and how they differ.

ApproachWhat it checksReadabilityNotes
Object.values(obj).includes(value)ValuesHighBest general readable option
Object.values(obj).indexOf(value) !== -1ValuesMediumOlder style, works but less clear
value in objKeysLow for this use caseWrong if you need to check values
Object.keys(obj).includes(key)KeysHighUseful only for key names
set.has(value)ValuesHighGreat when checking repeatedly

Enum-like object vs real TypeScript enum

OptionExampleNotes
Enum-like objectconst MESSAGE_TYPE = { INFO: 1, ... } as constCommon in modern codebases, simple runtime object
TypeScript enumenum MessageType { INFO = 1, ... }Built-in enum syntax, but some teams avoid it

Example with a real enum

enum MessageType {
  INFO = 1,
  SUCCESS = 2,
  WARNING = 3,
  ERROR = 4,
}

const type = 3;
const exists = Object.values(MessageType).includes(type);

Be careful: numeric enums in TypeScript generate reverse mappings at runtime, so Object.values() may include both names and numbers. That can be confusing for beginners. For many projects, an as const object is simpler and more predictable.

Take Quiz

Cheat Sheet

Quick check for values

Object.values(MESSAGE_TYPE).includes(value)

If input may be a string

Object.values(MESSAGE_TYPE).includes(Number(value))

Recommended enum-like object

const MESSAGE_TYPE = {
  INFO: 1,
  SUCCESS: 2,
  WARNING: 3,
  ERROR: 4,
} as const;

Reusable helper

function isMessageType(value: unknown): boolean {
  return Object.values(MESSAGE_TYPE).includes(Number(value));
}

Key vs value

  • "INFO" in MESSAGE_TYPE → checks a key
  • Object.values(MESSAGE_TYPE).includes(1) → checks a value

Prefer

includes()

over:

indexOf(...) !== -1

Common edge case

Object.values(MESSAGE_TYPE).includes("3") // false
Object.values(MESSAGE_TYPE).includes(Number("3")) // true

For repeated checks

const validTypes = new Set(Object.values(MESSAGE_TYPE));
validTypes.has(3);

FAQ

How do I check if a number exists in an enum-like object in TypeScript?

Use:

Object.values(MESSAGE_TYPE).includes(value)

This checks whether the value appears in the object's values.

Is in the right operator for this check?

No, not for values. in checks property names, not property values.

Should I use includes() or indexOf()?

Use includes() for readability. It directly expresses the idea of membership.

Why does checking "3" fail when 3 exists?

Because "3" is a string and 3 is a number. includes() uses strict equality.

Do I need Lodash for this?

Usually no. Native JavaScript methods like Object.values() and includes() are enough.

Should I use a real TypeScript enum instead?

You can, but many projects prefer as const objects because they are simpler at runtime and easier to reason about.

How can I make this reusable across my project?

Create a helper such as isMessageType(value) and call it wherever validation is needed.

Take Quiz

Mini Project

Description

Build a small message type validator that accepts input from an external source and decides whether it is a valid message type. This demonstrates how to validate enum-like values before using them in application logic.

Goal

Create a reusable validator and label function for message type codes.

Requirements

Create an enum-like MESSAGE_TYPE object with four numeric values. Write a function that checks whether an unknown input is a valid message type. Convert string inputs like "3" to numbers before validation. Write a function that returns a readable label for valid message types. Test the code with valid and invalid inputs.

Take Quiz

Keep learning

Related questions

View more