Function and Functional
The function category identifies and manipulates function types.
The functional category provides runtime helpers written in a functional style, plus the types that describe them.
The class category, covered at the end, handles the constructor side of the same thing.
IsFunction and IsNotFunction
Section titled “IsFunction and IsNotFunction”type IsFunction<T, $O extends $StrictOptions<$O, IsFunction.$Options> = {}>type IsNotFunction<T, $O extends $StrictOptions<$O, IsNotFunction.$Options> = {}>🎭 predicate — validates that T is Function or any function signature.
import type { IsFunction, IsNotFunction } from 'type-plus'
type R = IsFunction<Function> // truetype R = IsFunction<() => void> // true
type R = IsFunction<number> // falsetype R = IsFunction<never> // falsetype R = IsFunction<unknown> // false
type R = IsNotFunction<() => void> // falsetype R = IsNotFunction<number> // trueBoth are distributive, so a union that mixes functions and non-functions yields boolean:
type R = IsFunction<Function | number> // booleantype R = IsFunction<Function | number, { distributive: false }> // falseWith selection: 'filter' they become filters instead of predicates:
type R = IsFunction<(() => string) | number, { selection: 'filter' }> // () => stringtype R = IsNotFunction<(() => string) | number, { selection: 'filter' }> // numberWith exact: true they match only the type Function itself, not a function signature:
type R = IsFunction<Function, { exact: true }> // truetype R = IsFunction<() => void, { exact: true }> // falsetype R = IsFunction<(() => void) & { a: 1 }, { exact: true }> // false
type R = IsNotFunction<Function, { exact: true }> // falsetype R = IsNotFunction<() => void, { exact: true }> // trueUse it when the distinction between the bare Function type and a callable signature matters.
It replaces IsStrictFunction and IsNotStrictFunction, which were removed in 8.0.0.
They support the full option set — $any, $unknown, $never, $void, $then, $else,
selection, distributive, exact, and the $Branch selectors. See type branching
and options.
AnyFunction
Section titled “AnyFunction”type AnyFunction<Params extends any[] = any[], Result = any> = (...args: Params) => Result🧰 type util — a constraint for “any function”, with optional parameter and result types.
import type { AnyFunction } from 'type-plus'
function callTwice<F extends AnyFunction>(fn: F) { /* ... */}
type Handler = AnyFunction<[event: string], void> // (event: string) => voidExtractFunction
Section titled “ExtractFunction”type ExtractFunction<T extends AnyFunction>function extractFunction<T extends AnyFunction>(fn: T): ExtractFunction<T>⚗️ transform — pulls the call signature out of a composite type, dropping the extra properties.
import { extractFunction, type ExtractFunction } from 'type-plus'
type Composite = (() => void) & { a: 1 }
type R = ExtractFunction<Composite> // () => void
const fn = extractFunction((() => {}) as Composite) // () => voidIt works on an intersection of a function with other members. It does not work on overloads or unions.
ChainFn and EndoFn
Section titled “ChainFn and EndoFn”type ChainFn<T> = (param: T) => Ttype EndoFn<T> = (param: T) => TBoth describe a function that returns the same type it takes.
ChainFn names the chaining use case; EndoFn names the mathematical one — an endofunctor maps a category back to itself.
import type { ChainFn } from 'type-plus'
const trim: ChainFn<string> = (s) => s.trim()compose
Section titled “compose”function compose<FS extends AnyFunction[]>( ...fns: FS): (...args: Parameters<Head<FS>>) => ReturnType<Last<FS>>Composes functions left to right. Each function receives the previous function’s return value. The composed function takes the parameters of the first function and returns the result of the last.
import { compose } from 'type-plus'
const parseThenDouble = compose( (s: string) => Number.parseInt(s, 10), (n: number) => n * 2,)
parseThenDouble('21') // 42, typed (s: string) => numbercontext
Section titled “context”function context<Init extends ContextBaseShape, Ctx extends ContextBaseShape = Init>( init?: Init | (() => Init),): ContextBuilder<Init, Ctx>Builds a context object incrementally. Each extend() adds properties and widens the resulting type;
build() produces the accumulated context.
import { context } from 'type-plus'
const ctx = context({ id: 1 }) .extend((c) => ({ label: `item-${c.id}` })) .extend(() => ({ enabled: true })) .build() // { id: number; label: string; enabled: boolean }An extender returns only the new properties; the builder left-joins them onto the current context. Returning an existing property overrides its value.
Supporting types:
| Type | Description |
|---|---|
ContextBaseShape |
Record<string | symbol, any> — the constraint every context satisfies. |
ContextExtender<Current, Additional> |
(context: Current) => Additional — the shape of an extend() callback. |
ContextBuilder<Init, Ctx> |
The builder returned by context(), exposing extend() and build(). |
Classes
Section titled “Classes”A class is a function with a new signature, so the class category lives here.
AnyConstructor
Section titled “AnyConstructor”type AnyConstructor<Params extends any[] = any[]> = new (..._args: Params) => void🧰 type util — the AnyFunction of classes: a constraint for “any class”.
import type { AnyConstructor } from 'type-plus'
class Foo { constructor(_a: number) {}}
type R = typeof Foo extends AnyConstructor ? true : false // truetype R = Foo extends AnyConstructor ? true : false // false, `Foo` is the instance typetype R = typeof Foo extends AnyConstructor<[string]> ? true : false // falseThe return type is void, not the instance type. That is what lets it match every class regardless of
what it constructs. Use typeof TheClass, not TheClass: the bare name is the instance type.
isInstanceof
Section titled “isInstanceof”function isInstanceof<T extends AnyConstructor>( subject: unknown, classConstructor: T,): subject is InstanceType<T>🛡️ type guard — instanceof for a value typed unknown, narrowing it to the instance type.
import { isInstanceof } from 'type-plus'
function handle(err: unknown) { if (isInstanceof(err, TypeError)) { err.message // narrowed to TypeError }}Plain instanceof already narrows a typed value. This is for the unknown case, where writing the
predicate by hand means repeating the instance type.
isConstructor
Section titled “isConstructor”🗑️ removed in 8.0.0: no replacement. It called new on the subject and read the error message to
decide, so it returned true for any function that can be called with new, and an arrow function
could still pass after compilation. There is no reliable runtime test for this; check for what you
actually need instead. AnyConstructor stays.