Interface GetRefsOptions

Options type for z.getRefs

interface GetRefsOptions {
    depth?: number | boolean;
    strict?: boolean;
    loopSingles?: boolean;
    loopCount?: boolean;
    asyncUnwrap?: boolean;
    async?: number | boolean;
    filter?: FilterCallback<any, any, any>;
    map?: MapCallback<any, any, any, any>;
    mapKeys?: MapKeysCallback<any, any, any, any>;
    take?: number | TakeCallback<any, any, any>;
    props?: boolean | "all";
    string?: boolean;
    symbol?: boolean;
    own?: boolean;
    inherited?: boolean;
    top?: boolean;
    hidden?: boolean;
    enumerables?: boolean;
    nonEnumerables?: boolean;
    dataViewType?: DataViewType;
    sparse?: boolean;
    filterSingles?: boolean;
    singlesReject?: any;
}

Hierarchy (view full)

Properties

depth?: number | boolean
strict?: boolean

If true, only isMany input values (according to z.isMany or option.allProps) are allowed and iterated over (excluding WeakMap & WeakSet which can't be iterated at all). All other z.isSingle values (& WeakMap & WeakSet) throw an error.

If it is false (default), single & Weak inputs are "iterated over" as a single item iteration. All isSingle input values are iterated as-is, even falsey values, which means you can safely use them in a for...of loop(), on top of any code block that operates on isSingle or isMany values!

All isMany input values are iterated over props/indexes/items always the same way, regardless of this option.

false / undefined
loopSingles?: boolean

If true (default), isSingleOrWeak inputs are iterated as a single item iteration (of the input value itself).

If false, input values are omitted from the "iteration", i.e they are not iterated at all, a 0-item loop.

A soft alternative to strict: true that doesn't throw, but just omits the single values.

true
loopCount?: boolean

@todo: false NOT IMPLEMENTED YET

If true (default), iterating over iterators/generators also receive a count parameter: the number of items that have been emitted so far.

You can turn it off, if your iterator might exceed the number limit & and the small overhead of counting.

See count parameter in TitemsCallback

true
asyncUnwrap?: boolean

@todo: true NOT IMPLEMENTED yet - treats Promises as normal items. Till then you can await it: for (const [item, idx] of z.loop(promises) console.log(await item, idx)

If false (default), it treats nested items that are Promises, as normal items (i.e `for [item] of loop(promises) { item instanceOf Promise === true}

If true, it awaits each isPromise item and passes the resolved value as the item in the loop. It returns an AsyncGenerator, so the for...of must be awaited, for example:

const promises = [ Promise.resolve(1]),  Promise.resolve(2]),  Promise.resolve(3]), ]

for await (const [item, idx, count] of z.loop(promises, {asyncUnwrap: true, filter: oddNumbers})) {
item instanceOf Promise === false
_.isNumber(item) === true

console.log({item, idx, count})
}
// {item: 1, idx: 0, count: 1
// {item: 3, idx: 2, count: 2

Note: if input value is a Promise itself, it is NEVER awaited, but treated as an `isSingle` value. All you need to do is to `loop(await inputPromise)` though
false / undefined
async?: number | boolean

Async option: NOTE: it applies only when looping with z.each(), not the normal _.zloop()

If Async is enabled, then if iteratee callback returns a promise, it waits for it to resolve before continuing to the next element(s) & the each() call returns a Promise<input>, resolved when iteration is over.

Async is enabled in two ways: Auto Async & Forced Async

Auto Async (default, async: undefined / missing)

  • If async: undefined (default), async is "Auto Async": it goes into async mode automatically, if the iteratee is a real-runtime async function.
  // Auto Async mode, as iteratee is async

z.each(persons, async (person, idxKey) => registerPersonPromise(person))

// waits until each person is registered, before continuing to the next.

but

   // No async mode, as iteratee is not marked as async (not it returns a Promise)

z.each(persons, (person, idxKey) => {
registerPersonPromise(person)
return null
})

// DOES NOT wait until each person is registered, before continuing to the next. Effectively, all promises are dispatched simultaneously.

Note: there's a discrepancy between TypeScript's static evaluation of async functions and the actual runtime behavior. TypeScript will always treat as async function, any function determined to be returning a Promise, even if it is not marked as async function(...){}. For example:

   // Our iteratee is an implicit async function, in the TypeScript world, because the iteratee is returning a promise. Causes a discrepancy between TypeScript static compilation & JS runtime:

z.each(persons, (person, idxKey) => registerPersonPromise(person) )

// DOES NOT wait until each person is registered, before continuing to the next. Effectively, all promises are dispatched simultaneously.

As iteratee is not a real async function, we have no way of knowing to enable the Auto Async mode at runtime. But TypeScript reports it as a Promise returning function, hence at compile type it is treated as async. In short:

  • TS assumes we're returning a Promise<typeof input> as each() result
  • But at runtime, we have no way of knowing this is an async function, hence it is treated as sync (and each() returns plain input).

This can't be overcome AFAIK, SIJS (Such Is JavaScript). Just mark you promise-returning functions with async keyword, if you want to use Auto Async mode, or use async: true option below.

Forced Async (async: true)

  • If we have an explicit async: true, it is forced into async mode: it awaits on each callback return (if its a promise), before continuing to the next (and each() returns a Promise<input>). The each() call returns a Promise<input>, resolved when iteration is over.
   z.each(persons, (person, idxKey) => registerPersonPromise(person), {async: true})

// waits until each person is registered, before continuing to the next.
  • If we have an explicit async: number, it is forced into async mode with given N parallelism: it awaits on each callback return, but processes up to the number of N tasks in parallel, before continuing to a next item (and each() returns a Promise<input>). Note: async: true is equivalent to async: 1, i.e parallelism of 1 (see bellow). Warning: items will be processed in a non-stable order (cause of unpredictable Promises resolving). @todo: NOT IMPLEMENTED (any number is treated as true)
   z.each(persons, (person, idxKey) => registerPersonPromise(person), {async: 3})

// waits until each person is registered, up to 3 persons at a time, before continuing to a next person.
  • If we have an explicit async: false, it is forced into sync mode (even for real async functions). It does not await on each callback return, before continuing to the next (and each() returns the original input right away).

Note: if input value is a Promise itself, it is NEVER awaited, but treated as an isSingle value. All you need to do is to loop(await inputPromise) though!

undefined (Auto Async)
filter?: FilterCallback<any, any, any>

A filter callback, used to filter out items as they are iterated over.

If both filter and map are provided, filter is applied first, then map is applied to the result.

WARNING: If you return STOP from the filter callback, the loop will stop immediately, without calling the map callback.

WARNING: If you pass callbacks as a prop of options (eg loop(input, {props: true, map: (val, prop) => val *2 }), it will break the TypeScript type inference of the callback args. You should pass the callbacks as a separate argument in loop() parameters, eg loop(input, {props: true}, {map: (val, prop) => val *2 }) if you want to get the perfect TypeScript ingerence of the callback args. See IloopCallbacks

Note: It's best to NOT type your callback arguments and let the type system infer them from the context. If you do type them, make sure it's the right types, otherwise the loop() signature detection will fail!

map?: MapCallback<any, any, any, any>

A mapping callback (projection), used to map the items as they are iterated over. If both filter and map are provided, filter is applied first, then map is applied to the result.

WARNING: If you pass callbacks as a prop of options (eg loop(input, {props: true, map: (val, prop) => val *2 }), it will break the TypeScript type inference of the callback args. You should pass the callbacks as a separate argument in loop() parameters, eg loop(input, {props: true}, {map: (val, prop) => val *2 }) if you want to get the perfect TypeScript ingerence of the callback args. See IloopCallbacks

Note: It's best to NOT type your callback arguments and let the type system infer them from the context. If you do type them, make sure it's the right types, otherwise the loop() signature detection will fail! *

mapKeys?: MapKeysCallback<any, any, any, any>

@todo: NOT IMPLEMENTED/tested yet

A mapping function that can be used to map the keys that are iterated over. If both filter and map are provided, filter is applied first, then mapKeys is applied to the result.

WARNING: If you pass callbacks as a prop of options (eg loop(input, {props: true, map: (val, prop) => val *2 }), it will break the TypeScript type inference of the callback args. You should pass the callbacks as a separate argument in loop() parameters, eg loop(input, {props: true}, {map: (val, prop) => val *2 }) if you want to get the perfect TypeScript ingerence of the callback args. See IloopCallbacks

Note: It's best to NOT type your callback arguments and let the type system infer them from the context. If you do type them, make sure it's the right types, otherwise the loop() signature detection will fail!

take?: number | TakeCallback<any, any, any>

If take is a number, it limits the number of items that are iterated over. If take is a function, it stops the iteration the 1st time it returns false

If options.props & value is an _.isObject() other than realObject, it will iterate both on:

  • the first n keys/idx of the value (eg the Array elements, Set items etc)
  • the first n of props as well

WARNING: If you pass callbacks as a prop of options (eg loop(input, {props: true, map: (val, prop) => val *2 }), it will break the TypeScript type inference of the callback args. You should pass the callbacks as a separate argument in loop() parameters, eg loop(input, {props: true}, {map: (val, prop) => val *2 }) if you want to get the perfect TypeScript ingerence of the callback args. See IloopCallbacks

Note: No guaranteed order for property bags like Objects, Sets, Maps etc and return the same value type of input value.

Note: It's best to NOT type your callback arguments and let the type system infer them from the context. If you do type them, make sure it's the right types, otherwise the loop() signature detection will fail!

props?: boolean | "all"

If true, consider ONLY the props, instead of Nested Keys for special objects (Arrays, TypedArrays, Maps & Sets) - mimics Object.keys() / _.keys() if so.

By default, z.keys() returns the "natural keys" for special objects with nested values:

  • indexes for Array & TypedArray (eg [0, 1, 2, 3, ...])
  • the actual keys of Map & Set (eg ['someMapKey', {anObjectUsedAsKey: true}, Symbol('aSymbolKey')] instead of their Object props.

Similarly, all other functions like loop() & map() (that extend these options), iterate on those "natural keys" for all of these special objects.

For all other objects (including realObject, all Iterators & Generators, Boxed Primitives etc),

  • their string/symbol props are returned in keys()
  • are iterated on props on loop() and family, also depending on their other function-specific options.

If props: true then the keys() & iterations are only against props, even for special objects, instead of Nested Keys.

This only affects the special objects: it makes no difference to non-special values, if props is true/false, as their props are always considered anyway.

Note that Object.keys() always returns props for ALL objects (i.e props: true always), and there is no way to grab natural keys (or symbol props for that matter). This is a special feature of z.keys(), and much more useful: why would you want the props of Map or an Array anyway, 99% of the time? But if you subclassed Array or Map and want to get the props of that instance (instead of their natural keys), then you use props: true.

Finally, if props: 'all', you get both the props (first), followed by Nested Keys. Useful for print/debugging, comparisons etc.

@default: false / undefined

string?: boolean

Whether to include normal string props

@default: true

symbol?: boolean

Whether to include Symbol props, when we're dealing with a realObject or props: true

@default: false

own?: boolean

Whether to include own object props.

Note: It refers to props only, not Nested Keys, for arrays specifically. We include naturalKeys (i.e array Indexes) even when { own: false, props: 'all' }, to keep consistent with other Inspectable Nested Keys holders (Map & Set). But in realObjects, { own: false, props: any } will return no own props, as it should.

@default: true

inherited?: boolean

If true, it walks the prototype chain & retrieves all keys of inherited objects.

@default: false

top?: boolean

If true (and nonEnumerables is also true), it includes top-level (i.e Object) props like 'constructor', 'toString', 'valueOf', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', 'toLocaleString' etc.

It also retrieves these hidden props (that have no types definitions): `'defineGetter', 'defineSetter', lookupGetter_', 'lookupSetter', 'proto'.

@default: false

hidden?: boolean

If hidden: true (and nonEnumerables & top are also true), it includes top-level (i.e Object) hidden props like `'defineGetter', 'defineSetter', lookupGetter_', 'lookupSetter', 'proto'. Note that these are hidden props, thus have no TypeScript definitions.

Also, if hidden: true, it un-hides user/custom props that start & end with __, eg __myHiddenProp__ (which are hidden otherwise by default) @todo(363): filter user/custom __hidden__ props in KeysT/Values types in typescript

Note: hidden is a.k.a as private in the JS/TS world, but we use hidden to avoid confusion with the private keyword in TS and possible different semantics. Will revisit in future versions.

@default: false

enumerables?: boolean

Whether to include enumerable keys

true
nonEnumerables?: boolean

Whether to include enumerable keys

false
dataViewType?: DataViewType

When you loop() over an ArrayBuffer (or request keys() or values()), you must provide the dataViewType option (eg Int16, Float32 etc). The type of the items in the ArrayBuffer is required by the internal DataView that reads/writes to the ArrayBuffer.

Throws if dataViewType is missing and z.type(input) === 'ArrayBuffer'

sparse?: boolean

If true, it respects the position of Array items, assigning the mapped/filtered array items to the same indexes (i.e sparse Arrays, will stay sparse). If false, it will always return a dense/compact Array.

@default: false

filterSingles?: boolean

If filterSingles: false (default), it throws an error on any z.isSingleOrWeak input (eg z.filter(123) throws), as well as for WeakMap & WeakSet which are treated as singles (they can't be iterated over).

Otherwise, if filterSingles: true see singlesReject option for explanation.

true
singlesReject?: any

If filterSingles: false (default), it throws an error on any single input (eg z.filter(123) throws).

BUT:

If filterSingles: true, it returns a "clone" of the single value itself, if it passes the filterCb!

So, if filterCb passes:

  • Primitives are returned as is
  • BoxedPrimitive instances & Boxed single values (eg Date, RegExp) are returned a new instance of the same type, with same underlying value. This is so it's consistent with the other collection functions (map, clone etc), which return new instances of the same type.

Now, if filterCb doesn't pass, the return value depends on singlesReject option:

If singlesReject is truthy, it returns that value. Note that the default is the special value z.NOTHING (which equals to Symbol('z.NOTHING'))

If singlesReject is falsey, it returns what ever this value is (eg undefined) in most cases, except:

  • number becomes NaN
  • BigInt also becomes NaN
  • Symbol becomes z.NOTHING (i.e Symbol('z.NOTHING'))
  • Date becomes new Date(null), which is 1970-01-01T00:00:00.000Z
  • Function becomes a () => EMPTY (i.e () => Symbol('z.NOTHING'))

Boxed Primitives (please, please DONT use them!) are returned as new instances of the same type, with an "invalid" value:

  • Number becomes new Number(NaN)
  • Boolean becomes new Boolean(false) (which is bad!), but who uses Boolean instances anyway?
  • String becomes new String('') - also bad to use!

Note: All other _.isObject single input values (eg Date, RegExp, BoxedPrimitive instances) are returned as a new instance of same type, even if filterCb rejects them.

@default: z.NOTHING (i.e Symbol('z.NOTHING'))