Skip to content

Repository files navigation

Type Utils

Type utilities module for Typescript and also Javascript. It can secure your application from invalid data being pushed inside and breaking things as it can shape and model your data to prevent invalid data. Check out the documentation for further details.

GitHubNpm package versionNpm package total downloadsNpm package licenseBiomeVitest

Table of Contents

Installing

With NPM:

npm install @srhenry/type-utils --save

With Yarn:

yarn add @srhenry/type-utils

Docs

See: API - Github Pages

Schema types

It represents a string to typescript's type infers and runtime validation

import{string}from'@srhenry/type-utils'constisString=string()//any stringconstisAvocadoString=string('avocado')//specific stringconstisPatternString=string(/goo+gle/)//pattern/RegExp matched string

Supports the fluent .max(), .min(), .regex(), .nonEmpty(), .url(), .email(), .optional(), .validator(), .use(), and .toStandardSchema() APIs.

It represents a number to typescript's type infers and runtime validation

import{number}from'@srhenry/type-utils'constisNumber=number()

Supports the fluent .nonZero(), .max(), .min(), .optional(), .validator(), .use(), and .toStandardSchema() APIs.

It represents a number to typescript's type infers and runtime validation

import{boolean}from'@srhenry/type-utils'constisBoolean=boolean()

Supports the fluent .optional(), .validator(), .use(), and .toStandardSchema() APIs.

It represents a well defined object to typescript's type infers and runtime validation, which its properties are also described using the Schema helpers

import{object,string,number}from'@srhenry/type-utils'constisMyObject=object({foo: string(),bar: number(),})

Supports the fluent .optional(), .validator(), .use(), and .toStandardSchema() APIs.

It represents an array to typescript's type infers and runtime validation, which its items can also be described using the Schema helpers

import{array,string,object}from'@srhenry/type-utils'constisArray=array()// array of anythingconstisMyArray=array(string())// array of stringsconstisMyObjArray=array(object({foo: string('bar')}))constisMyObjArray2=array({foo: string('bar')})

Supports the fluent .max(), .min(), .unique(), .optional(), .validator(), .use(), and .toStandardSchema() APIs.

It represents a fixed-length tuple to typescript's type infers and runtime validation. Each element is validated by its own schema.

import{tuple,string,number}from'@srhenry/type-utils'constisTuple=tuple(string(),number(),string())// TypeGuard<[string, number, string]>// Also accepts an array of schemas:constisPoint=tuple([number(),number()])// TypeGuard<[number, number]>

Tuples support StandardSchemaV1 inputs as entries and the fluent .optional(), .validator(), .use(), and .toStandardSchema() APIs.

It represents a record (key-value map) to typescript's type infers and runtime validation. Key and value guards can be specified for type-safe records.

import{record,string,number}from'@srhenry/type-utils'constisRecord=record()// Record<string, any>constisStringMap=record(string(),string())// Record<string, string>constisNumKeyed=record(number(),number())// Record<number, number>

Records support string | number | symbol key types, StandardSchemaV1 inputs for key/value guards, and the fluent .nonEmpty(), .optional(), .validator(), .use(), and .toStandardSchema() APIs.

It represents a symbol to typescript's type infers and runtime validation

import{symbol}from'@srhenry/type-utils'constisSymbol=symbol()

Supports the fluent .optional(), .validator(), .use(), and .toStandardSchema() APIs.

It represents a bigint to typescript's type infers and runtime validation.

import{bigint}from'@srhenry/type-utils'constisBigint=bigint()constisPositiveBigint=bigint().min(0n)constisNonZeroBigint=bigint().nonZero()

Supports the fluent .nonZero(), .max(n: bigint), .min(n: bigint), .optional(), .validator(), .use(), and .toStandardSchema() APIs.

It represents a closed switch values to typescript's type infers and runtime validation. It ensures your value is one of the values given in params.

import{asEnum}from"@srhenry/type-utils"enumStatus{ready: 1,running: 2,stopped: 3,}constvalidStatus=[...Object.keys(Status)]as(keyoftypeofStatus)[]constisStatus=asEnum(validStatus)

Supports the fluent .optional(), .validator(), .use(), and .toStandardSchema() APIs.

It represents a null literal to typescript's type infers and runtime validation.

import{asNull}from'@srhenry/type-utils'constisNull=asNull()

Supports the fluent .optional(), .validator(), .use(), and .toStandardSchema() APIs.

It represents an undefined literal to typescript's type infers and runtime validation.

import{asUndefined}from'@srhenry/type-utils'constisUndefined=asUndefined()

Supports the fluent .optional(), .validator(), .use(), and .toStandardSchema() APIs.

It represents a primitive values (such as string, number, boolean, symbol, null, undefined) to typescript's type infers and runtime validation.

import{primitive}from'@srhenry/type-utils'constisSymbol=primitive()

Supports the fluent .optional(), .validator(), .use(), and .toStandardSchema() APIs.

It represents a 'any' value to typescript's type infers and runtime validation. It does nothing to validate a narrowed type but can be useful to improve readability in more complex schemas.

import{any,object}from'@srhenry/type-utils'constisAny=any()constobjectHasFoo=object({foo: isAny})//it checks if is object and if has `foo` property but doesn't care checking its type

Supports the fluent .optional(), .validator(), .use(), and .toStandardSchema() APIs.

Schema.optional

Since v0.5.0, this method was removed, but also embeded in all exported schemas.

Since v0.6.0, this method was replaced to be a property in the returned type guard instead of a property of the schema.

It represents a optional value to typescript's type infers and runtime validation. You can access this schema by acessing the .optional property in the desired optional schema:

import{object,string,number}from'@srhenry/type-utils'constobjectMaybeHasFoo=object({foo: string().optional(),bar: number().optional()})// it checks if is object and if has `foo`.// if it has `foo` then check if it is string or undefined,// if it hasn't then pass anyway as it is optional property.

When using object<T>({...}), include every key from T in the schema tree. Optional properties in T must still be present in the tree with .optional() so the runtime validator knows which properties may be omitted:

import{object,string}from'@srhenry/type-utils'interfaceItem{id: stringname?: string}constisItem=object<Item>({id: string(),name: string().optional()})isItem({id: 'item-1'})// true

Schema helpers

It creates an intersection between two schemas.

import{object,string,and}from'@srhenry/type-utils'consthasFoo=object({foo: string()})consthasBar=object({bar: string()})constisSomething=and(hasFoo,hasBar)

Supports the fluent .optional(), .validator(), .use(), and .toStandardSchema() APIs.

It creates an union between two schemas.

import{string,boolean,or}from'@srhenry/type-utils'constisString=string()constisBool=boolean()constisSomething=or(isString,isBool)

Supports the fluent .optional(), .validator(), .use(), and .toStandardSchema() APIs.

It wraps a schema (just to improve readability).

import{object,string,array,useSchema}from'@srhenry/type-utils'consthasFoo=object({foo: string()})constisFooArray=array(useSchema(hasFoo))

Validation rules

It constraints a number to be different from 0.

import{number}from'@srhenry/type-utils'constisNonZeroNumber=number().nonZero()

It constraints a number to be lesser than a given number.

import{number}from'@srhenry/type-utils'constisNonZeroNumber=number().max(255)

It constraints a number to be greater than a given number.

import{number}from'@srhenry/type-utils'constisNonZeroNumber=number().min(1)

It constraints an array's size to be lesser than a given number.

import{array,any}from'@srhenry/type-utils'constisArray=array(any()).max(25)

It constraints an array's size to be greater than a given number.

import{array,any}from'@srhenry/type-utils'constisArray=array(any()).min(2)

It constraints an array to contain only distinct values, failling if a duplicate is found.

import{array,string}from'@srhenry/type-utils'constisArray=array(string()).unique()

It constraints a string's size to be lesser than a given number.

import{string}from'@srhenry/type-utils'constisString=string().max(60)

It constraints a string's size to be greater than a given number.

import{string}from'@srhenry/type-utils'constisString=string().min(10)

It constraints a string to match a given pattern (regular expression).

import{string}from'@srhenry/type-utils'constisNumericString=string(/[0-9]+/)// or using fluent pattern:constisNumericString2=string().regex(/[0-9]+/)

It constraints a string's size to be greater than 0.

import{string}from'@srhenry/type-utils'constisString=string().nonEmpty()

It constraints a string to be a valid url representation.

import{string}from'@srhenry/type-utils'constisStringUrl=string().url()

It constraints a string to be a valid email representation.

import{string}from'@srhenry/type-utils'constisStringEmail=string().email()

It constrains a record to not be empty.

import{record}from'@srhenry/type-utils'constisNonEmptyRecord=record().nonEmpty()

It constraints a bigint to be different from 0n.

import{bigint}from'@srhenry/type-utils'constisNonZeroBigint=bigint().nonZero()

It constraints a bigint to be lesser than or equal to a given bigint.

import{bigint}from'@srhenry/type-utils'constisMaxBigint=bigint().max(100n)

It constraints a bigint to be greater than or equal to a given bigint.

import{bigint}from'@srhenry/type-utils'constisMinBigint=bigint().min(1n)

Schema.use

Since v0.6.0

It allows you to create custom validation rules to be used in schemas.

import{string,createRule}from'@srhenry/type-utils'constStringNumber=createRule({name: "Custom.StringNumber",message: "number",handler: (value: string)=>()=>!Number.isNaN(Number(value)),});constisStringNumber=string().use(StringNumber())

Schema.validator

Since v0.6.1

It allows you to get a validator instance to validate a value against the schema. By default, the validator throws on failure. Pass false to get a ValidateReturn<T> instead.

import{string,object}from'@srhenry/type-utils'constvalidator=object({name: string().min(1)}).validator()validator({name: 'Alice'})// { name: 'Alice' } — passes, returns the valuevalidator({name: ''})// throws ValidationErrorsconstsoftValidator=object({name: string().min(1)}).validator(false)softValidator({name: 'Alice'})// { name: 'Alice' }softValidator({name: ''})// ValidationErrors (no throw)// Also available as .validate on the validator:validator.validate({name: 'Bob'})// { name: 'Bob' }

Standard Schema Interop

Since v0.7.0

type-utils implements the Standard Schema v1 specification for interoperability with other validation libraries (Zod, Valibot, ArkType, etc.).

import{toStandardSchema,fromStandardSchema,normalizeSchema,isStandardSchema}from'@srhenry/type-utils/standard-schema'importtype{StandardSchemaV1,InferInput,InferOutput}from'@srhenry/type-utils/standard-schema'

Converts a TypeGuard<T> into a StandardSchemaV1<T, T>. Also available as a fluent method on all schemas:

import{string}from'@srhenry/type-utils'import{toStandardSchema}from'@srhenry/type-utils/standard-schema'constguard=string()conststandard=toStandardSchema(guard)// or using the fluent API:conststandard2=string().toStandardSchema()constresult=standard['~standard'].validate('hello')// { success: true, value: 'hello' }

Converts a StandardSchemaV1<Input> into a synchronous TypeGuard<Input>. Throws if the external schema's validate() returns a Promise (async schemas cannot be converted to synchronous type guards).

import{fromStandardSchema}from'@srhenry/type-utils/standard-schema'import{z}from'zod'constzodString=z.string()constguard=fromStandardSchema(zodString)// TypeGuard<string>

Accepts either a TypeGuard<T> or a StandardSchemaV1<T, T> and returns a TypeGuard<T>. Useful when building APIs that accept both type-utils guards and external Standard Schema validators:

import{normalizeSchema}from'@srhenry/type-utils/standard-schema'import{object,string}from'@srhenry/type-utils'import{z}from'zod'constschema=object({name: normalizeSchema(string()),age: normalizeSchema(z.number()),})

Type guard that checks if a value implements the StandardSchemaV1 interface (has the '~standard' property).

Type guard that checks if a value is a native type-utils TypeGuard<T> (has internal struct metadata). Useful for distinguishing type-utils guards from external Standard Schema validators:

import{string,isNativeSchema}from'@srhenry/type-utils'import{isStandardSchema}from'@srhenry/type-utils/standard-schema'constguard=string()isNativeSchema(guard)// trueisStandardSchema(guard)// true (type-utils guards also implement Standard Schema)import{z}from'zod'isNativeSchema(z.string())// falseisStandardSchema(z.string())// true

The core type representing a Standard Schema v1 compliant validator. Includes the StandardSchemaV1 namespace with Props, Result, SuccessResult, FailureResult, Issue, and Options types. Utility types InferInput<T> and InferOutput<T> extract the input/output types from a StandardSchemaV1.

Available validations

It checks a given value against a given schema or validator and return true if schema matches the value, otherwise return false.

import{string,is}from'@srhenry/type-utils'//...if(is(value,string())){// value is string}else{// value is not a string}

It checks a given value against a given schema or validator and returns the checked value with schema inferred type if schema matches the value or throws an error if schema didn't match the value. Pretty clean to use with destructuring pattern.

import{object,number,string,ensureInterface}from'@srhenry/type-utils'//...const{ foo, bar }=ensureInterface(value,object({foo: number(),bar: string(),})//It throws an error if validation fails!console.log('foo',foo)// fooconsole.log('bar',bar)// bar

NOTE: You can use schema directly to validate a value.

import{object,number}from"@srhenry/type-utils"consthasFoo=object({foo: number()}))//...if(hasFoo(obj)){// obj is object and contains a string property named `foo`}else{// obj don't have a `foo` property of type string}

Match Pattern

Create a new reusable pattern matcher object or an inline pattern matcher object for a given value.

Reusable matcher example:

import{match,object,string,array,pick}from'@srhenry/type-utils'constmatcher=match().with(object({foo: string().min(3)}),obj=>`Foo is valid: '${obj.foo}'`).with(string().nonEmpty(),str=>`String is valid: '${str}'`).default(()=>{thrownewTypeError('😭')})matcher.exec('foo')//String is valid: 'foo'matcher.exec({foo: 'bar'})//Foo is valid: 'bar'matcher.exec({foo: 'ba'})//TypeError: 😭matcher.exec("")//TypeError: 😭

Inline matcher example:

import{match,object,string,array,pick}from'@srhenry/type-utils'//API endpoint processing example:const{ data }=payloadconstresponses=awaitPromise.allSettled(match(data).with(object({events: array()}),({ events })=>events.map(e=>match(e).with(object({type: string("USER_EVENT"),uid: string()}),({type: _, uid, ...data})=>UserEvent.send({from:uid, ...data})).with(object({type: string("SYSTEM_EVENT")}),({type: _, ...data})=>SystemEvent.process({ ...data})).default(()=>null).exec())).default(()=>[]).exec().filter(Boolean)).then(results=>results.map(result=>match(result).with(object({status: string("fulfilled")}),({ value })=>parseToResponse(value)).with(object({status: string("rejected")}),({ reason })=>{log.error('Event processing failed:',reason)switch(reason.type){case'UserEventError':
//specific handling for user event errorsreturnUserEventError.from(reason)break;case'SystemError':
//specific handling for system event errorsreturnSystemError.from(reason)break;default:
returnreason}}).exec()))returnnewJsonResponse({success:true,event_responses: responses})//formatted response

Util Types

It represents a synchronous function type. It has two type parameters: the first is a tuple representing the function parameters types, and the second is the return type of the function.

importtype{Fn}from'@srhenry/type-utils'declareconstfn: Fn<[number,number],number>// fn: (arg_0: number, arg_1: number) => number

It represents an asynchronous function type. It has two type parameters: the first is a tuple representing the function parameters types, and the second is the resolved return type of the function.

importtype{AsyncFn}from'@srhenry/type-utils'declareconstfn: AsyncFn<[number,number],number>// fn: (arg_0: number, arg_1: number) => Promise<number>

It represents a synchronous function type that does not return any value (void). It has one type parameter: a tuple representing the function parameters type.

importtype{Action}from'@srhenry/type-utils'declareconstfn: Action<[string]>// fn: (arg_0: string) => void

It represents a predicate function type. It has one type parameter: a tuple representing the function parameters type.

importtype{Predicate}from'@srhenry/type-utils'declareconstfn: Predicate<[any]>// fn: (arg_0: any) => boolean

It represents an result type tuple. It has two type parameters: the first is the success type, and the second is the failure type (optional, default Error).

importtype{Result}from'@srhenry/type-utils'declareconstres1: Result<number>// res1: [null, number] | [Error, null]declareconstres2: Result<string,TypeError>// res2: [null, string] | [TypeError, null]

It represents an asynchronous result type tuple. It has two type parameters: the first is the success type, and the second is the failure type (optional, default Error).

importtype{AsyncResult}from'@srhenry/type-utils'declareconstres1: AsyncResult<number>// res1: [null, number] | [Error, null]declareconstres2: AsyncResult<string,TypeError>// res2: [null, string] | [TypeError, null]

It slices a tuple type. It has three type parameters: the first is the tuple type to be sliced, the second is the start index (inclusive), and the third is the end index (exclusive, optional, default to tuple length).

importtype{TupleSlice}from'@srhenry/type-utils'declareconstres1: TupleSlice<[number,string,boolean],1>// res1: [string, boolean]declareconstres2: TupleSlice<[number,string,boolean],1,2>// res2: [string]

It extracts the type of a function parameter by its index. It has two type parameters: the first is the function type, and the second is the parameter index.

importtype{Param}from'@srhenry/type-utils'declareconstres1: Param<(a: number,b: string)=>void,0>// res1: numberdeclareconstres2: Param<(a: number,b: string)=>void,1>// res2: string

It infers the type from a type guard function. It has one type parameter: the type guard function type.

import{typeInfer,array}from'@srhenry/type-utils'declareconstisArray=array()declareconstres1: Infer<(a: unknown)=>a is number>// res1: numberdeclareconstres2: Infer<(a: unknown)=>a is string[]>// res2: string[]declareconstres3: Infer<typeofisArray>// res3: any[]

Maps a TypeScript type T to the FluentSchema result type you'd get from calling the corresponding schema builder function. Useful for building generic utilities that need to know the schema shape from a type.

importtype{GetSchema}from'@srhenry/type-utils'typeT1=GetSchema<string>// FluentSchema<string, StringSchemaRules>typeT2=GetSchema<number>// FluentSchema<number, NumberSchemaRules>typeT3=GetSchema<boolean>// FluentSchema<boolean>typeT4=GetSchema<string[]>// FluentSchema<string[], ArraySchemaRules>typeT5=GetSchema<{foo: string}>// FluentSchema<{ foo: string }>typeT6=GetSchema<never>// never

It creates a nominal/branded type by attaching a unique tag to a base type. Useful for distinguishing types that share the same underlying structure.

importtype{Tag}from'@srhenry/type-utils'typeUserId=Tag<string,'UserId'>typeOrderId=Tag<string,'OrderId'>declarefunctiongetUser(id: UserId): voiddeclarefunctiongetOrder(id: OrderId): voidconstuserId='abc'asUserIdgetUser(userId)// ok// getOrder(userId) // type error — UserId is not OrderId

It has two required type parameters: the base type and the tag name (a PropertyKey). An optional third parameter Metadata defaults to void.

Experimental Features

This was inspired in C# Lambdas, equivalent to arrow functions in Javascript/Typescript, but this helper adds invoke() method to a function instance. useful to improve readability when you have a function that returns another and you wanna call 'em all in a row, using fluent pattern.

import{Experimental}from'@srhenry/type-utils'const{ lambda }=Experimentalfunctionbuilder(locales: string|string[]='en-US'){functionformatter(options: Intl.DateTimeFormatOptions={dateStyle: 'short',timeStyle: 'short',}){functionformat(date: Date|string){returnnewIntl.DateTimeFormat(locales,options).format(newDate(date))}returnlambda(format)}returnlambda(formatter)}console.log('1970-01-01T00:00 =',builder('en-UK').invoke({dateStyle: 'long'}).invoke('1970-01-01'))// 01 January 1970

This does type-wisely curries a function or lambda, in two flavors: allowing or not partial param applying (default is not allowed). The process of currying a function is traditionally a techique that allows you to call the refered function passing one parameter at a time, returning another function to further apply remaining parameters, then returning whatever the original function returns after all parameters were given to curried function. In Javascript this techinque usually allows partial apply, and in that way you can pass more than one parameter at a time, and everything else remains equal to the traditional currying.

import{Experimental}from'@srhenry/type-utils'const{ lambda, curry }=Experimental// lets reuse earlier example:functionbuilder(locales: string|string[],options: Intl.DateTimeFormatOptions,date: Date|string){returnnewIntl.DateTimeFormat(locales,options).format(newDate(date))}constcurried=curry(builder)constcurriedLambda=curry(lambda(builder))console.log(curried('en-GB')({timeStyle: 'short',timeZone: 'Etc/Greenwich'})(newDate('2020-05-10T22:35:08Z')))// 22:35console.log(curriedLambda('en-US').invoke({dateStyle: 'short',timeStyle: 'short',timeZone: 'America/New_York'}).invoke(newDate('2020-05-10T22:35:08Z')))// (EDT) 5/10/20, 6:35 PM

This is a fluent API to create sync/async function pipelines. Inspired in FP pipe operator while it does not comes to Javascript/Typescript yet. It allows only single param functions, piping the return as the parameter to the next function in pipeline.

Breaking changes (experimental API):

  • inject() has been removed — use callWith() for reverse-apply or a plain transform () => value for value replacement
  • .pipe(enpipe(value)) as reverse-apply is no longer idiomatic — use .pipe(callWith(value)) instead
  • .pipe(fn => fn(value)) lambda pattern is replaced by .pipe(callWith(value))
  • .pipe(enpipe(fn, ...args)) is replaced by .pipe(apply(fn, ...args))
  • GetPipeline<T> type has been removed — use Pipe<T> instead
  • PipelineBox and AsyncPipelineBox are now standalone classes (not decorator-based)
  • AsyncPipelineBox no longer has .catch() — use .depipe().catch() on the promise

pipe(value) / createPipeline(fn?)

Creates a PipelineBox<T> (sync) or AsyncPipelineBox<T> (async) from a value or function:

import{Experimental}from'@srhenry/type-utils'const{ pipe }=Experimentalconstresult=pipe('hello').pipe(s=>s.toUpperCase()).pipe(s=>s.split('')).depipe()// ['H', 'E', 'L', 'L', 'O']

When a Promise is passed, the pipeline becomes async:

constresult=awaitpipe(Promise.resolve(42)).pipe(n=>n*2).depipe()// 84

.pipe(transform)

Applies a transform function to the boxed value. If the transform returns a Promise, the pipeline auto-promotes to AsyncPipelineBox:

constresult=pipe(5).pipe(n=>n*10)// PipelineBox<number>.pipe(n=>Promise.resolve(n+1))// AsyncPipelineBox<number>.depipe()// Promise<51>

.pipe(callWith(...args))

Reverse-apply: calls the boxed function with the given arguments. Replaces the fn => fn(value) lambda pattern:

import{Experimental}from'@srhenry/type-utils'const{ pipe, callWith }=Experimentalconstgreet=(name: string)=>`Hello, ${name}!`constresult=pipe(greet).pipe(callWith('World')).depipe()// 'Hello, World!'

With multi-arg functions:

constadd=(a: number,b: number)=>a+bconstresult=pipe(add).pipe(callWith(3,4)).depipe()// 7

.pipe(apply(fn, ...args))

Partial application: curries fn with the given args, returning a transform that applies the next incoming value to the remaining parameters:

import{Experimental}from'@srhenry/type-utils'const{ pipe, apply }=Experimentalconstmultiply=(a: number,b: number)=>a*bconstresult=pipe(5).pipe(apply(multiply,3))// applies 3 as first arg, incoming 5 as second.depipe()// 15

tap(fn, options?) / tapAsync(fn, options?)

Side-effect factories that pass the value through unchanged. Errors are swallowed by default (swallow: true). Use .catch(handler) to route errors:

const{ pipe, tap }=Experimentalconstresult=pipe('hello').pipe(tap(v=>console.log(v))).pipe(tap(()=>{thrownewError('boom')},{catch: e=>console.error(e)})).pipe(s=>s.toUpperCase()).depipe()// 'HELLO'

Fluent .tap() / .tapAsync() are also available directly on the box:

constresult=pipe('hello').tap(v=>console.log(v)).pipe(s=>s.toUpperCase()).depipe()

enpipe(value) / enpipe(fn, ...args)

Deprecated since v0.8.0 — use callWith() and apply() instead for clearer, more type-safe transforms.

Creates a dual-callable Pipe<T> — works as both a standalone chainable and a .pipe() transform. When used in a pipeline, the boxed function is called with the incoming value (reverse-apply). When called standalone, it returns its boxed value:

import{Experimental}from'@srhenry/type-utils'const{ enpipe, callWith }=Experimentalconstp=enpipe('hello')p.depipe()// 'hello'p('unused')// 'hello' (callable)p.pipe(s=>s.toUpperCase()).depipe()// 'HELLO'

With partial application:

constcurried=enpipe(addUserFactory,db)// curries first argcurried.depipe()// the curried function

Realistic async pipeline example

import{Experimental}from'@srhenry/type-utils'const{ pipe, callWith, apply, tap }=ExperimentalconstaddUserFactory=(db: Record<string,Record<string,any>[]>)=>(user: Record<string,any>)=>newPromise<string>(resolve=>{setTimeout(()=>{constid=uuid()db['users']??=[]db['users']?.push({ id, ...user})resolve(id)},200)})constaddPostFactory=(db: Record<string,Record<string,any>[]>)=>(user_id: string,post: Record<string,any>)=>newPromise<boolean>(resolve=>{setTimeout(()=>{db['posts']??=[]db['posts']?.push({ user_id, ...post})resolve(true)},300)})constdb={users: []asRecord<string,any>[],posts: []asRecord<string,any>[],}asRecord<string,Record<string,any>[]>[]constaddPostCurried=(post: Record<string,any>)=>(id: string)=>pipe(addPostFactory).pipe(callWith(db)).pipe(callWith(id,post)).depipe()constresult=awaitpipe(addUserFactory).pipe(callWith(db)).pipe(callWith({name: 'Marcus',email: 'example@email.com'})).pipeAsync(addPostCurried({title: 'Hello World',content: 'Lorem ipsum dolor sit amet',})).pipeAsync(()=>{if(db['users']!.length===0||db['posts']!.length===0)returnfalsedb['replies']=[]returntrue}).depipe()// true | false

@deprecated since 0.6.2.

Use the new match() factory instead.

This helper enables you to build switch expressions as it is not available in Javascript vanilla. Each branch allows you to define the matchers or values ahead of time with literal values or inline expressions, or define with callbacks to customize handling of each branch, making it a powerfull way to describe a complex switch without if-else-if language syntax. It defines a lambda as the switch runner, so you can define and run it in the row with more readability.

constswitcher=$switch().case(4,'four').case(3,'three').case(2,'two').case(1,'one').default('none of the above')// it does not run yetconsole.log(switcher.invoke(1))// oneconsole.log(switcher.invoke(3))// threeconsole.log(switcher.invoke(10))// none of the above
constswitcher=$switch(5).case(4,'four').case(3,'three').case(2,'two').case(1,'one').default('none of the above')// it does not run yetconsole.log(switcher())// none of the aboveconsole.log(switcher.invoke())// none of the aboveconsole.log(switcher.invoke(1))// none of the aboveconsole.log(switcher(3))// none of the above
constswitcher=$switch<number>().case(n=>n%2===0,()=>Math.floor(Math.random()*10_000)+1).default(n=>n**n)console.log(switcher(1))// 1 (1^1)console.log(switcher(2))// random number between 1-10000console.log(switcher(3))// 27 (3^3)console.log(switcher(4))// random number between 1-10000console.log(switcher(5))// 3125 (5^5)console.log(switcher(6))// random number between 1-10000console.log(switcher(7))// 823543 (7^7)

About

Type utilities for Typescript and also Javascript

Resources

Stars

4 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages