JavaScript some in TypeScript

September 19, 202611 min read
Requirements:
ArraysFunctions| Unions

The JavaScript some method answers one question: does at least one element in this array pass my test? It returns true or false, it stops at the first match, and it has been in the language since ES5.

The runtime behaviour takes one example. The half worth reading is what TypeScript does with it: which types the callback parameters get, why some hands back a plain boolean and refuses to narrow anything, and why its sibling every can narrow when some cannot.

What JavaScript some does

Run a predicate over the elements. Return true as soon as one of them passes.

const temperatures = [12, 18, 27, 9]
 
const hasHotDay = temperatures.some((temp) => temp > 25) // true
const hasFreezingDay = temperatures.some((temp) => temp < 0) // false

The callback stops running the moment it returns something truthy. With 27 at index 2, the fourth element is never visited. That short-circuit is the reason some beats filter(...).length > 0filter always walks the whole array and allocates a new one to throw away.

const scores = [40, 91, 55]
 
const passedSome = scores.some((score) => score >= 90) // ✅ stops at 91
const passedFilter = scores.filter((score) => score >= 90).length > 0 // ❌ same answer, more work

The signature TypeScript uses

Here is the declaration from the standard library:

interface SomeExample<T> {
  some(predicate: (value: T, index: number, array: T[]) => unknown, thisArg?: unknown): boolean
}

Three things follow from it.

The callback parameters are inferred, so you never annotate them. value is the element type, index is a number, and array is the array you called it on.

const users = [
  { name: 'Ada', active: true },
  { name: 'Alan', active: false },
]
 
const anyActive = users.some((user, index, all) => {
  // user: { name: string; active: boolean }
  // index: number
  // all: { name: string; active: boolean }[]
  return user.active && index < all.length
})

The predicate returns unknown, not boolean. That is deliberate — it lets you return a truthy value instead of a real comparison, the way plain JavaScript does.

type Contact = { name: string; email?: string }
 
const contacts: Contact[] = [{ name: 'Ada', email: 'ada@example.com' }, { name: 'Alan' }]
 
const anyEmail = contacts.some((contact) => contact.email) // ✅ string | undefined is fine

That looseness has a cost, which the async section below gets to. If you want the full picture of how a callback type like this is written and read, see function types.

The last parameter, thisArg, rebinds this inside a function callback. With arrow functions it does nothing useful. Ignore it.

some returns a boolean and narrows nothing

This is the part that trips people up. Compare the two signatures:

interface NarrowingComparison<T> {
  some(predicate: (value: T, index: number, array: T[]) => unknown, thisArg?: unknown): boolean
  every<S extends T>(
    predicate: (value: T, index: number, array: T[]) => value is S,
    thisArg?: unknown,
  ): this is S[]
}

every has an overload that returns this is S[]. some has no such overload, and it never will. The reason is logic, not a gap in the type definitions: "every element is a string" tells you the array is string[]. "One element is a string" tells you nothing about the array as a whole.

In practice:

function isString(value: unknown): value is string {
  return typeof value === 'string'
}
 
const mixed: (string | number)[] = ['a', 1, 'b']
 
if (mixed.every(isString)) {
  mixed // string[] ✅ narrowed
}
 
if (mixed.some(isString)) {
  mixed // (string | number)[] — unchanged, and correctly so
}

The every narrowing only kicks in when you call it on a variable you can name. Call it on the result of an expression and there is no this to narrow, so you get the boolean overload instead. Union types covers what these narrowed element types are made of.

When you want the element rather than a yes/no answer, some is the wrong method. Reaching for find gets you the value and the narrowing in one pass:

const inventory = [
  { sku: 'a-1', stock: 0 },
  { sku: 'b-2', stock: 4 },
]
 
const inStock = inventory.find((item) => item.stock > 0)
 
if (inStock) {
  inStock.sku // ✅ narrowed from the undefined branch
}

Using some to check and then find to fetch walks the array twice and gives you nothing extra.

some vs every vs includes vs find

MethodQuestion it answersReturnsNarrows?
someDoes any element pass?booleanNo
everyDo all elements pass?booleanYes, with a predicate
includesIs this exact value present?booleanNo
findWhich element passes?T | undefinedYes, via the result

includes compares values, some runs a function. That distinction matters more than it looks, because includes is strict about the type of what you hand it — and some is not.

const ROLES = ['admin', 'editor'] as const
 
declare const role: string
 
// ROLES.includes(role)
// ❌ TS2345: Argument of type 'string' is not assignable to
//    parameter of type '"admin" | "editor"'.
 
const isKnownRole = ROLES.some((known) => known === role) // ✅ compiles

ROLES is a readonly ['admin', 'editor'], so includes demands one of those two literals. A plain string is not one. The some version compiles because comparing 'admin' | 'editor' with string is a legal comparison — the types overlap. This is the everyday reason to pick some over includes when you are checking a widened value against a literal list.

You will still meet indexOf(value) !== -1 in older code. It is the pre-ES2016 way of writing includes, it carries the same strict argument type, and it treats NaN as absent. Where the check is a comparison rather than an equality test, some is the one that reads like what it does.

Empty arrays: some is false, every is true

const nothing: number[] = []
 
const someResult = nothing.some((value) => value > 0) // false
const everyResult = nothing.every((value) => value > 0) // true

some needs a witness and cannot find one, so it is false. every finds no counterexample, so it is true — vacuous truth, and a genuine source of bugs when you validate a list that might be empty. If an empty list should fail your check, test the length yourself:

declare const uploads: { valid: boolean }[]
 
const allValid = uploads.length > 0 && uploads.every((upload) => upload.valid)

some also skips holes in sparse arrays, so [, ,].some(() => true) is false. You are unlikely to meet a sparse array on purpose, but that is the behaviour if you do.

An async predicate always returns true

This one is silent, which makes it the worst of the bunch.

declare function isBlocked(userId: string): Promise<boolean>
 
const userIds = ['u1', 'u2']
 
const anyBlocked = userIds.some(async (id) => await isBlocked(id))
// true — always, regardless of what isBlocked resolves to

some is synchronous. An async callback returns a Promise, every promise is truthy, so the first element always "passes". And because the signature accepts unknown, TypeScript does not complain — this is exactly the loophole that lets contact.email work as a predicate.

Resolve the promises first, then ask the question:

declare function checkBlocked(userId: string): Promise<boolean>
 
async function hasBlockedUser(ids: string[]) {
  const results = await Promise.all(ids.map((id) => checkBlocked(id)))
  return results.some(Boolean) // ✅ boolean[] answered synchronously
}

Promise.all collapses the list of promises into a list of values, and some takes it from there. Note that this loses the short-circuit — every check runs. That is the price of doing them concurrently, and it is usually the right trade.

Readonly arrays and tuples

some is declared on ReadonlyArray too, so as const data and readonly parameters work without a cast:

const PORTS = [80, 443, 8080] as const
 
const hasTls = PORTS.some((port) => port === 443) // ✅ boolean
 
function anyAbove(limit: number, values: readonly number[]) {
  return values.some((value) => value > limit) // ✅ readonly is fine
}

The element type inside the callback is the union of the tuple's members — 80 | 443 | 8080 in the first example — which is why comparing against 443 passes and comparing against 21 would be flagged as a comparison with no overlap. Taking readonly number[] in your own signatures is the habit worth keeping: it accepts both mutable and readonly arrays, and some never mutates anything.

Checking objects, not arrays

some is an array method, so an object needs converting first. Object.values is the short way when the keys do not matter, and Object.entries is the one to reach for when they do.

type FormErrors = {
  email?: string
  password?: string
  username?: string
}
 
const formErrors: FormErrors = { password: 'Too short' }
 
const hasAnyError = Object.values(formErrors).some(Boolean) // true
const failedFields = Object.entries(formErrors)
  .filter(([, message]) => message !== undefined)
  .map(([field]) => field)
// ['password'] — string[]

Object.values(formErrors) is (string | undefined)[], and passing Boolean straight in as the predicate works because the predicate returns unknown. Writing .some((message) => !!message) means the same thing if you prefer it spelled out.

The keys come back as plain string, not as the union 'email' | 'password' | 'username'. That is a deliberate choice in the type definitions rather than a bug — Object.entries explains why, and how to get the key union back when you need it.

some over a discriminated union

Discriminated unions and some fit together well, because the predicate body gets the full narrowing that the method itself refuses to hand out.

type Job =
  | { status: 'queued' }
  | { status: 'running'; startedAt: Date }
  | { status: 'failed'; error: string }
 
const jobs: Job[] = [{ status: 'queued' }, { status: 'failed', error: 'timeout' }]
 
const anyFailed = jobs.some((job) => job.status === 'failed')
const anyStale = jobs.some(
  (job) => job.status === 'running' && Date.now() - job.startedAt.getTime() > 60_000,
)

In the second predicate, job.startedAt is only reachable after the status check, and TypeScript knows it. The && narrows job to the running member, and the property exists on that member — no optional chaining, no cast.

What you do not get is anything outside the callback. anyFailed is a boolean, and jobs is still Job[] in the branch below it. To act on the failures you need the elements themselves:

type Task =
  | { status: 'ok' }
  | { status: 'failed'; error: string }
 
declare const tasks: Task[]
 
const failures = tasks.filter((task) => task.status === 'failed')
// { status: 'failed'; error: string }[] — inferred predicate, TypeScript 5.5+

Since TypeScript 5.5, filter infers a type predicate from a callback like that one, so failures carries the narrowed member type without you writing task is ... by hand. some gets no such upgrade, because there is no narrowed type for it to return.

Writing your own some

If you need some over something that is not an array — a Set, a Map, an iterator — there is no built-in. A small generic helper covers it:

function someOf<T>(items: Iterable<T>, predicate: (value: T) => unknown): boolean {
  for (const item of items) {
    if (predicate(item)) return true
  }
  return false
}
 
const tags = new Set(['ts', 'js'])
const hasTs = someOf(tags, (tag) => tag === 'ts') // true

Same short-circuit, same unknown return on the predicate, and it works on anything iterable. Note what it cannot do: it returns boolean, not a type predicate, for the same reason the built-in does not.

Summary

The JavaScript some method is a short-circuiting existence check, and TypeScript types it about as plainly as a method can be typed. Four things to carry away:

For the other everyday array methods and what TypeScript infers from them, flatMap covers mapping and flattening in one pass, and the Filter challenge is the type-level version of the same idea if you want to practise. Object.entries is the equivalent page for objects.

Share this article

Become a TypeScript Pro

Track your progress through 100+ hands-on challenges. Free, sign in with GitHub.

Or start solving right away: explore all TypeScript challenges

Practice with Challenges

Put your javascript some in typescript knowledge to the test with these related challenges.

#18220Filter
Medium
#898Includes
Easy
#21104FindAll
Medium

Related Concepts

Concepts that build on or relate to javascript some in typescript.

flatMap in TypeScriptObject.entries in TypeScriptTypeScript Function TypesUnion TypesPromise.all in TypeScript