Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 23 additions & 7 deletions .size-limit.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,42 +2,42 @@
{
"name": "es5-full",
"path": "lib/dist/es5/mod/ts-utils.js",
"limit": "33.5 kb",
"limit": "35 kb",
"brotli": false,
"running": false
},
{
"name": "es6-full",
"path": "lib/dist/es6/mod/ts-utils.js",
"limit": "32 kb",
"limit": "33.5 kb",
"brotli": false,
"running": false
},
{
"name": "es5-full-brotli",
"path": "lib/dist/es5/mod/ts-utils.js",
"limit": "12 kb",
"limit": "12.5 kb",
"brotli": true,
"running": false
},
{
"name": "es6-full-brotli",
"path": "lib/dist/es6/mod/ts-utils.js",
"limit": "11.5 kb",
"limit": "12 kb",
"brotli": true,
"running": false
},
{
"name": "es5-zip",
"path": "lib/dist/es5/mod/ts-utils.js",
"limit": "13 Kb",
"limit": "13.5 Kb",
"gzip": true,
"running": false
},
{
"name": "es6-zip",
"path": "lib/dist/es6/mod/ts-utils.js",
"limit": "13 Kb",
"limit": "13.5 Kb",
"gzip": true,
"running": false
},
Expand Down Expand Up @@ -107,9 +107,25 @@
{
"name": "es5-scheduleMicrotask",
"path": "lib/dist/es5/mod/ts-utils.js",
"limit": "1.5 kb",
"limit": "1.75 kb",
"import": "{ scheduleMicrotask }",
"gzip": true,
"running": false
},
{
"name": "es5-scheduleNextTick",
"path": "lib/dist/es5/mod/ts-utils.js",
"limit": "2 kb",
"import": "{ scheduleNextTick }",
"gzip": true,
"running": false
},
{
"name": "es5-scheduleNextTickAndMicrotask",
"path": "lib/dist/es5/mod/ts-utils.js",
"limit": "2 kb",
"import": "{ scheduleNextTick, scheduleMicrotask }",
"gzip": true,
"running": false
}
]
8 changes: 7 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,17 @@

### Features

- Add microtask scheduling helpers with native `queueMicrotask`, Promise, and timer-backed fallbacks
- [#570](https://github.com/nevware21/ts-utils/pull/570) Add microtask scheduling helpers with native `queueMicrotask`, Promise, and timer-backed fallbacks
- New functions: `scheduleMicrotask`, `hasQueueMicrotask`, `getQueueMicrotask`, `setMicroTaskFallbackOptions`
- New public types: `ScheduleMicrotaskFn`, `MicroTaskOptions`
- Extends microtask support by providing cancellable microtasks via `ITimerHandler`, plus fallback ordering to run microtasks before queued timers when using the timer-backed implementation
- Provides runtime parity across all supported environments by using native `queueMicrotask` when present, Promise-backed scheduling when available, and a timer-backed microtask queue otherwise
- [#573](https://github.com/nevware21/ts-utils/pull/573) Add nextTick scheduling support and new helper exports
- New timer/runtime functions: `scheduleNextTick`, `getProcessNextTick`, `hasProcessNextTick`, `setNextTickFallbackOptions`
- Uses native `process.nextTick` in Node runtimes when available, while providing equivalent nextTick behavior in browser and worker runtimes via Promise and timer-backed fallbacks
- Adds shared queue logic and callback-argument support across microtask and nextTick scheduling
- Adds `arrConcat` as a dedicated array helper and `fnBindArgs` as a dedicated function helper
- Improves function binding typing with exported `BoundFunction` and updated signatures for `fnBind` / `fnBindArgs`

# v0.14.0 May 18th, 2026

Expand Down
10 changes: 5 additions & 5 deletions README.md

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions lib/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,10 @@
"interval",
"microtask",
"queueMicrotask",
"nextTick",
"process-nexttick",
"cancellable microtask",
"function-bind",
"includes",
"string contains",
"html encode",
Expand Down Expand Up @@ -147,6 +150,7 @@
"array-reduce",
"array-filter",
"array-find",
"array-concat",
"object-copy",
"object-keys",
"lightweight",
Expand Down
45 changes: 45 additions & 0 deletions lib/src/array/concat.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* @nevware21/ts-utils
* https://github.com/nevware21/ts-utils
*
* Copyright (c) 2026 NevWare21 Solutions LLC
* Licensed under the MIT license.
*/

import { ArrProto } from "../internal/constants";
import { _unwrapFunction } from "../internal/unwrapFunction";

/**
* Alias of `Array.prototype.concat()` for consumers that prefer the `arr*` helper naming.
*
* Unlike {@link arrAppend}, this helper does NOT mutate the input arrays and returns a new array.
* @since 0.15.0
* @function
* @group Array
* @param theArray - The array to concatenate into a new array.
* @param items - Additional arrays and/or items to concatenate.
* @returns A new array containing values from `theArray` followed by each concatenated item.
* @example
* ```ts
* let values = arrConcat([1], [2, 3]);
* // values is [1, 2, 3]
*
* let next = arrConcat(values, ["a", "b"]);
* // next is [1, 2, 3, "a", "b"]
* // values is still [1, 2, 3]
* ```
* @example
* ```ts
* const left = [1, 2];
* const right = [3, 4];
* const merged = arrConcat(left, right, 5);
* // merged is [1, 2, 3, 4, 5]
* // left is still [1, 2]
* // right is still [3, 4]
* ```
*/
export const arrConcat: {
<T>(theArray: T[]): T[];
<T>(theArray: T[], ...items: ConcatArray<T>[]): T[];
<T>(theArray: T[], ...items: (T | ConcatArray<T>)[]): T[];
} = (/*#__PURE__*/_unwrapFunction("concat", ArrProto));
44 changes: 44 additions & 0 deletions lib/src/funcs/fnBindArgs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* @nevware21/ts-utils
* https://github.com/nevware21/ts-utils
*
* Copyright (c) 2026 NevWare21 Solutions LLC
* Licensed under the MIT license.
*/

import { BoundFunction } from "./types";

/**
* Creates a new bound function using `fn.bind()` where the pre-bound arguments are supplied as an
* array or tuple value, similar to how {@link fnApply} supplies arguments to invocation.
*
* This is useful when the argument list is already available as an array or tuple value,
* but you want to return a reusable bound function rather than invoke immediately.
* @param fn - The function instance to bind.
* @param thisArg - The value to be used as the `this` when calling `fn`.
* @param argArray - Optional array or tuple value containing arguments to pre-bind to `fn`.
* @returns A new bound function.
* @since 0.15.0
* @group Function
* @example
* ```ts
* const module1 = {
* prefix: "Hello",
* log(value: string, punctuation: string) {
* return this.prefix + " " + value + punctuation;
* }
* };
*
* const module2 = {
* prefix: "Hi"
* };
*
* const bound = fnBindArgs(module1.log, module2, ["friend"]);
* bound("!"); // "Hi friend!"
* ```
*/
export function fnBindArgs<F extends (...args: any[]) => any, T, TArgs extends any[]>(fn: F, thisArg: T, argArray: TArgs): BoundFunction<F, TArgs>;
export function fnBindArgs<F extends (...args: any[]) => any, T>(fn: F, thisArg: T): F;
export function fnBindArgs<F extends (...args: any[]) => any, T>(fn: F, thisArg: T, argArray?: any[]): any {
return fn.bind.apply(fn, (argArray ? [ thisArg ].concat(argArray) : [ thisArg ]) as any);
}
35 changes: 32 additions & 3 deletions lib/src/funcs/funcs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
*/

import { ArrSlice, CALL } from "../internal/constants";
import { BoundFunction } from "./types";

/**
* The `fnApply` function calls the specified `fn` function with the given `thisArg` as the `this` value,
Expand Down Expand Up @@ -213,7 +214,7 @@ export function fnCall<F extends (...args: any) => any, T>(fn: F, thisArg: T): R
* module2.getX(); // 21
* ```
*/
export function fnBind<F extends Function, T>(fn: F, thisArg: T, ...argArray: any[]): F;
export function fnBind<F extends (...args: any[]) => any, T>(fn: F, thisArg: T): F;

/**
* Creates a new function that when called will set the value of `thisArg` as the `this` keyword
Expand Down Expand Up @@ -249,6 +250,34 @@ export function fnBind<F extends Function, T>(fn: F, thisArg: T, ...argArray: an
* module2.getX(); // 21
* ```
*/
export function fnBind<F extends Function, T>(fn: F, thisArg: T): F {
return fn.bind.apply(fn, ArrSlice[CALL](arguments, 1));
export function fnBind<F extends (...args: any[]) => any, T, TArgs extends any[]>(fn: F, thisArg: T, ...argArray: TArgs): BoundFunction<F, TArgs>;

/**
* Fallback overload for less-specific `Function`-typed call sites where the concrete parameter
* list is not available, such as dynamic proxy wiring.
* @param fn - The function instance to be called.
* @param thisArg - The value to be used as the `this` when calling the `fn`.
* @param argArray - Zero or more arguments to pre-bind to the function.
* @returns The original function type with the bound `this` value and any provided arguments.
* @since 0.9.8
* @group Function
* @example
* ```ts
* const host = {
* prefix: "Hello",
* log(value: string) {
* return this.prefix + " " + value;
* }
* };
*
* const dynamicLog: Function = host.log;
* const bound = fnBind(dynamicLog, host);
*
* bound("friend"); // "Hello friend"
* ```
*/
export function fnBind<F extends Function, T>(fn: F, thisArg: T, ...argArray: any[]): F;

export function fnBind<F extends (...args: any[]) => any, T>(fn: F, thisArg: T): any {
return fn.bind.apply(fn, ArrSlice[CALL](arguments, 1) as any);
}
32 changes: 32 additions & 0 deletions lib/src/funcs/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,35 @@ export type ProxyFunctionDef<T, H> = {
*/
rp?: boolean
}

/**
* Produces the return type for a function after a prefix of arguments has already been bound.
*
* This implementation is a TypeScript 3.4+ compatible middle ground between:
* 1. A fully simplified fallback (`(...args: any[]) => ReturnType<TFn>`)
* 2. A TS 4.0+ variadic tuple implementation (shown below as comments)
*
* It preserves remaining argument inference for up to 5 pre-bound arguments and then
* falls back to a generic callable return type.
* @since 0.15.0
* @group Funcs
* @typeParam TFn - The function being bound.
* @typeParam TArgs - The tuple of arguments that will be pre-bound.
*/
export type BoundFunction<TFn extends (...args: any[]) => any, TArgs extends any[]> =
TArgs extends [] ? TFn :
TArgs extends [any] ? (TFn extends (a0: any, ...args: infer R) => infer RT ? (...args: R) => RT : (...args: any[]) => ReturnType<TFn>) :
TArgs extends [any, any] ? (TFn extends (a0: any, a1: any, ...args: infer R) => infer RT ? (...args: R) => RT : (...args: any[]) => ReturnType<TFn>) :
TArgs extends [any, any, any] ? (TFn extends (a0: any, a1: any, a2: any, ...args: infer R) => infer RT ? (...args: R) => RT : (...args: any[]) => ReturnType<TFn>) :
TArgs extends [any, any, any, any] ? (TFn extends (a0: any, a1: any, a2: any, a3: any, ...args: infer R) => infer RT ? (...args: R) => RT : (...args: any[]) => ReturnType<TFn>) :
TArgs extends [any, any, any, any, any] ? (TFn extends (a0: any, a1: any, a2: any, a3: any, a4: any, ...args: infer R) => infer RT ? (...args: R) => RT : (...args: any[]) => ReturnType<TFn>) :
(...args: any[]) => ReturnType<TFn>;
// Requires TypeScript 4.0+ for variadic tuple types (this is a better alternative for the fnBind and fnBindArgs return types)
// /**
// * Produces the return type for a function after a prefix of arguments has already been bound.
// * @since 0.15.0
// * @group Funcs
// * @typeParam TFn - The function being bound.
// * @typeParam TArgs - The tuple of arguments that will be pre-bound.
// */
// export type BoundFunction<TFn extends (...args: any[]) => any, TArgs extends any[]> = TFn extends (...args: [...TArgs, ...infer TRemaining]) => infer TReturn ? (...args: TRemaining) => TReturn : never;
5 changes: 4 additions & 1 deletion lib/src/helpers/environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,10 @@ export const getHistory = (/*#__PURE__*/_getGlobalInstFn<History>(getInst, ["his
* @returns True if you are
*/
export const isNode = (/*#__PURE__*/_getGlobalInstFn<boolean>(() => {
return !!safe(() => (process && (process.versions||{}).node)).v;
return !!safe(() => {
let processInst = getInst<any>("process");
return processInst && (processInst.versions || {}).node;
}).v;
}));

/**
Expand Down
5 changes: 4 additions & 1 deletion lib/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
/// <reference lib="es2015" />

export { arrAppend } from "./array/append";
export { arrConcat } from "./array/concat";
export { ArrPredicateCallbackFn, ArrPredicateCallbackFn2, ArrMapCallbackFn, ArrFlatMapCallbackFn, ArrFromMapFn } from "./array/callbacks";
export { arrAt } from "./array/at";
export { arrChunk } from "./array/chunk";
Expand Down Expand Up @@ -46,9 +47,10 @@ export { arrUnzip } from "./array/unzip";
export { arrWith } from "./array/with";
export { arrZip } from "./array/zip";
export { fnApply, fnBind, fnCall } from "./funcs/funcs";
export { fnBindArgs } from "./funcs/fnBindArgs";
export { createFnDeferredProxy, createProxyFuncs } from "./funcs/fnProxy";
export { readArgs } from "./funcs/readArgs";
export { ProxyFunctionDef, TypeFuncNames } from "./funcs/types";
export { BoundFunction, ProxyFunctionDef, TypeFuncNames } from "./funcs/types";
export { WritableArrayLike } from "./helpers/arrayLike";
export {
isTypeof, isUndefined, isNullOrUndefined, isDefined, isString, isFunction, isObject, isArray, isArrayLike, isDate, isNumber, isBoolean,
Expand Down Expand Up @@ -170,6 +172,7 @@ export {
hasQueueMicrotask, scheduleMicrotask, getQueueMicrotask,
ScheduleMicrotaskFn, MicroTaskOptions, setMicroTaskFallbackOptions
} from "./timer/microtask";
export { hasProcessNextTick, scheduleNextTick, getProcessNextTick, NextTickOptions, ProcessNextTickFn, setNextTickFallbackOptions } from "./timer/nextTick";
export {
TimeoutOverrideFn, ClearTimeoutOverrideFn, TimeoutOverrideFuncs, scheduleTimeout, scheduleTimeoutWith,
createTimeout, createTimeoutWith, setTimeoutOverrides, setGlobalTimeoutOverrides
Expand Down
Loading