API: Guantr.prototype.can.any
The can.any sub-method performs a batch permission check — it returns true if any check in the array is granted. It resolves the evaluation context once and shares it across all checks, and short-circuits on the first true result.
Use this for decisions that require at least one permission (e.g. "should we show the interaction bar?").
Signature
ts
can.any(
checks: Array<[action: string, resource: [resourceKey: string, resourceInstance: object]]>,
): Promise<boolean>;Parameters
checks: An array of check tuples. Each tuple is[action, [resourceKey, resourceInstance]]:action: (string) The action being checked.resource: A tuple[string, object]of resource key and resource instance.
Returns
Promise<boolean>:trueas soon as any check is granted (short-circuits).falseif no check is granted.falsefor an empty array (vacuous false — no check in an empty set can pass).
How it works
- Resolves context once. The
contextoption is resolved once and its result is shared across every check in the array. - Iterates through the checks in order.
- For each check, performs the same evaluation as
can(): queries relevant rules, evaluates conditions against the resource instance and shared context. - Short-circuits on the first
true. Remaining checks are not evaluated. - Returns
falseif no check passed.
Empty array behavior
An empty array of checks always returns false (vacuous false):
ts
await guantr.can.any([]); // falseContext sharing
The context is resolved exactly once per can.any() call, not once per individual check.
ts
const guantr = await createGuantr({
context: async () => {
console.log('context called');
return { userId: 1 };
},
});
await guantr.can.any([
['read', ['post', { id: 1 }]],
['comment', ['post', { id: 1 }]],
]);
// "context called" logged only onceCaching
Individual check results within a can.any call are not independently cached — the raw evaluation path is called directly without consulting the cache. Subsequent individual can() calls may still hit the cache.
Short-circuit behavior
Because can.any stops on the first true:
- Place the most likely-to-pass checks first for best performance.
- If you expect most checks to be denied, expect near-full iteration.
- Later checks might never run — avoid side effects in check definitions.
Examples
Basic usage
ts
const post = { id: 1, title: 'Hello', status: 'published' };
// Check if user can interact with this post in any way
const canInteract = await guantr.can.any([
['read', ['post', post]],
['comment', ['post', post]],
['share', ['post', post]],
]);
// true if at least ONE action is allowedShort-circuit: first passing check
ts
// 'read' is allowed, so 'delete' and 'archive' are never evaluated
const result = await guantr.can.any([
['delete', ['post', post]], // false → continue
['read', ['post', post]], // true → short-circuit, return true
['archive', ['post', post]], // never evaluated
]);
// trueWhen no rules exist
ts
await guantr.can.any([
['read', ['post', { id: 1 }]],
['comment', ['post', { id: 1 }]],
]);
// false — no rules exist, all checks failWith overlapping allow/deny
ts
// Even if 'delete' is denied, 'read' may still be allowed
const result = await guantr.can.any([
['delete', ['post', { status: 'published' }]], // denied
['read', ['post', { status: 'published' }]], // allowed → true
]);
// trueMixed resource keys
ts
const result = await guantr.can.any([
['read', ['post', { id: 1 }]],
['read', ['document', { id: 5 }]],
]);See also
can.all— Check if all permissions are granted.cannot.all— Check if all permissions are denied (!can.any()).cannot.any— Check if any permission is denied (!can.all()).can— Single permission check.