Check if a TypeScript Array Contains a Specific Value

To check if a TypeScript array contains a specific value, call includes(): cities.includes("Denver") returns true or false. That works for strings, numbers and booleans. For an array of objects it will not, because includes() compares by reference, so you test a property with some() instead: orders.some(o => o.id === "ORD-1001"). This guide covers both, plus case-insensitive and partial matches, checking several values at once, the as const error that sends people here, and what to use when the array is long.

Every example was compiled and run with TypeScript 5.2.2 and Node.js v22.22.2 in the Windows Command Prompt, with tsc --target es2022 --lib es2023,dom --strict file.ts and then node file.js. The output is the real output. Reference: Array.prototype.includes() on MDN.

Check if a TypeScript array contains a value

includes() answers the question directly and reads as the sentence you were thinking. It takes an optional second argument, the index to start from, which is occasionally handy and often surprising:

const cities: string[] = ["Austin", "Denver", "Seattle"];

console.log(cities.includes("Denver"));        // true
console.log(cities.includes("Chicago"));       // false

const totals: number[] = [249.99, 89.5, 430];
console.log(totals.includes(430));

// start the search from a position, as indexOf does
console.log(cities.includes("Austin", 1));     // false: the search starts at index 1

// the old way, still everywhere in existing code
console.log(cities.indexOf("Seattle") !== -1);

Output:

true
false
true
false
true
Command Prompt showing TypeScript includes returning true and false for city names and a number, plus the indexOf equivalent
includes() gives you a boolean; indexOf() !== -1 is the older spelling.
What you needUse
Does it contain this value?items.includes(value)
Does it contain an object with this id?items.some(i => i.id === id)
Where is it?items.indexOf(value) or findIndex()
Give me the matching itemitems.find(i => ...)
Ignoring caseitems.some(i => i.toLowerCase() === v.toLowerCase())
Any or all of several valueswanted.some(...) / wanted.every(...)
Many lookups on a long arraynew Set(items).has(value)

includes() versus indexOf(): NaN and holes

These two are not interchangeable. indexOf() uses strict equality, and NaN === NaN is false, so it can never find one. includes() uses SameValueZero, which treats NaN as equal to itself, and it also sees the empty slots in a sparse array as undefined:

const readings: number[] = [12.5, NaN, 7];

console.log(readings.includes(NaN));           // true
console.log(readings.indexOf(NaN));            // -1: indexOf uses ===

console.log(readings.some(Number.isNaN));      // the explicit way to ask

const sparse: (number | undefined)[] = [1, 2, 3];
delete sparse[1];                               // a hole, not an undefined value

console.log(sparse.includes(undefined));        // true: includes treats a hole as undefined
console.log(sparse.indexOf(undefined));         // -1: indexOf skips holes

console.log([0].includes(-0), Object.is(0, -0));

Output:

true
-1
true
true
-1
true false
Command Prompt output showing includes finding NaN while indexOf returns minus one, and includes finding a hole in a sparse array
The two differences in one screen: NaN, and empty slots.

Arrays of objects: use some()

includes() asks “is this exact object in the array”, which is almost never the question. Two objects with identical properties are different values, so test a property instead. If you want the object rather than a yes or no, find() is the one you want:

interface Order {
  id: string;
  city: string;
}

const orders: Order[] = [
  { id: "ORD-1001", city: "Austin" },
  { id: "ORD-1002", city: "Denver" },
];

// includes() compares references, so an equal-looking object is not found
console.log(orders.includes({ id: "ORD-1001", city: "Austin" }));

// the one you already hold IS found
console.log(orders.includes(orders[0]));

// for objects, test a property instead
console.log(orders.some(order => order.id === "ORD-1001"));
console.log(orders.some(order => order.city === "Seattle"));

// and when you need the object itself, not just a yes or no
const found = orders.find(order => order.id === "ORD-1002");
console.log(found?.city ?? "not found");

Output:

false
true
true
false
Denver
Command Prompt output showing includes failing to find an equal-looking object while some matches on the id property
First line false, third line true: same data, different question.

Case-insensitive and partial matches

Two different questions get confused here. includes() on the array asks whether an element equals your value; some() with includes() on each element asks whether any element contains your text. Trim input that came from a form, and lowercase both sides when case should not matter, as in a search box over an array of objects:

const cities: string[] = ["Austin", "Denver", "Seattle"];

// case-insensitive: compare like with like
const input = "denver";
console.log(cities.some(city => city.toLowerCase() === input.toLowerCase()));

// "contains a value" versus "contains a value that contains this text"
console.log(cities.includes("Aus"));                                  // false: no exact match
console.log(cities.some(city => city.includes("Aus")));               // true: partial match

// does the array contain ANY of these, or ALL of them?
const wanted = ["Denver", "Chicago"];
console.log(wanted.some(city => cities.includes(city)));
console.log(wanted.every(city => cities.includes(city)));

// trim before comparing when the value came from a form
const typed = "  Seattle ";
console.log(cities.includes(typed), cities.includes(typed.trim()));

Output:

true
false
true
true
false
false true

The as const error you probably came for

Declaring a list of allowed values with as const makes it a readonly array of literal types. That is what you want for the type, but then includes() refuses your plain string, because a string is not one of those three literals:

const roles = ["admin", "editor", "viewer"] as const;

function isRole(value: string): boolean {
  return roles.includes(value);          // value is a plain string
}

console.log(isRole("admin"));

tsc output:

contains_union_error.ts(4,25): error TS2345: Argument of type 'string' is not assignable to parameter of type '"admin" | "editor" | "viewer"'.
Command Prompt showing TypeScript error TS2345 when calling includes with a string on a readonly array of literal types
The compiler is right: value might be any string at all.

The fix is a type guard. Widen the array to readonly string[] for the call, and declare the return type as value is Role so the caller gets the narrowed type for free:

const roles = ["admin", "editor", "viewer"] as const;
type Role = (typeof roles)[number];      // "admin" | "editor" | "viewer"

// a type guard: it answers the question AND narrows the value for the caller
function isRole(value: string): value is Role {
  return (roles as readonly string[]).includes(value);
}

const input = "editor";

if (isRole(input)) {
  const role: Role = input;              // narrowed, no cast needed
  console.log("valid role:", role);
}

console.log(isRole("admin"), isRole("owner"));

Output:

valid role: editor
true false

Long arrays and repeated lookups

includes() scans from the start every time. One lookup on a short array is nothing; thousands of lookups on a long one add up, and that is when a Set earns its keep, exactly as it does when collecting unique values:

const ids: string[] = Array.from({ length: 50_000 }, (_, i) => `ID-${i}`);
const lookups: string[] = Array.from({ length: 5_000 }, (_, i) => `ID-${i * 9}`);

function time(label: string, fn: () => number): void {
  fn();                                   // warm-up run
  const start = performance.now();
  const hits = fn();
  console.log(label.padEnd(24), (performance.now() - start).toFixed(1).padStart(8), "ms", "found", hits);
}

time("includes()", () => lookups.filter(id => ids.includes(id)).length);

const set = new Set(ids);
time("Set.has()", () => lookups.filter(id => set.has(id)).length);
Command Prompt output timing includes against Set has for five thousand lookups in an array of fifty thousand ids
Same answer: one scans, the other looks up.

Build the Set once, outside the loop. Building it inside the predicate costs more than the scan you were trying to avoid.

More TypeScript array guides worth reading:

Frequently asked questions

How do I check if a TypeScript array contains a value?

Use items.includes(value). It returns true or false and works for strings, numbers and booleans.

Why does includes() not find my object?

It compares by reference. Two objects with the same properties are different values, so use items.some(i => i.id === id).

What is the difference between includes() and indexOf()?

includes() returns a boolean and can find NaN; indexOf() returns a position and uses strict equality, so it cannot.

How do I check if an array contains a value ignoring case?

items.some(item => item.toLowerCase() === value.toLowerCase()). Trim the value too if it came from user input.

Why does includes() give an error with an as const array?

The array holds literal types, and your argument is a wider string. Use a type guard that casts to readonly string[] internally.

How do I check whether an array contains any of several values?

wanted.some(value => items.includes(value)) for any, and wanted.every(...) for all of them.

Is includes() slow on a large array?

It scans the array each time. For many lookups, build a Set once and call has() instead.