Advance type utilities for TypeScript.
Each type and function has an icon associated with it. They indicate what kind of feature they offer:
- 🚦 assertion (:vertical_traffic_light:): assertion function
- 💀 deprecated (:skull:): deprecated and will be removed soon
- 🛡️ guard (:shield:): type guard function
- 💥 immediate (:boom:): The effect of the type can be observed immediately during development.
- 🌪️ filter (:tornado:): a.k.a. parse These types perform some kind of test. If the input passes the test, the input is returned. Otherwise, it returns
never
(other icons considered: ↪️👉🚋⏩🐾🔑🚪💂🧲🙅♂️🪚). - 🏃 runtime (:runner:): The function has runtime effect
- 🩳 shortcut (:shorts:): Shortcut or convenient types
- 🧪 testing (:test_tube:): The type or function are designed for test.
- ⚗️ transform (:alembic:): These types transforms the input to another category.
- 🦴 utilities (:bone:): provide various functionalities (other icons considered: 🔧🔨).
- 🎭 predicate (:performing_arts:): a.k.a. validate or logical. These types perform some kind of test. If the input passes the test, it returns
true
orfalse
(other icons considered: ⭕).
npm install type-plus
yarn add type-plus
pnpm add type-plus
Type assertion is one of the main features of type-plus
.
Type assertions can be immediate
compile time check, or they can have runtime
behavior.
There are 4 kinds of type assertions:
logical
: functions or generic types that returnstrue
orfalse
type to be used in type level programming.filter
: generic type that returnsnever
if the test fails.
Equal<A, B>
:
IsEqual<A, B>
:
✔️ logical
Check if A
and B
are the same.
NotEqual<A, B>
:
IsNotEqual<A, B>
:
✔️ logical
Check if A
and B
are not the same.
IsExtend<A, B>
:
IsNotExtend<A, B>
:
✔️ logical
Check if A
extends or not extends B
.
Extendable<A, B>
:
NotExtendable<A, B>
:
✔️ filter
Check if A
extends or not extends B
.
IsAssign<A, B>
:
CanAssign<A, B>
:
✔️ logical
Check if A
can be assigned to B
.
A typical usage is using it with assertType
:
assertType.isFalse(false as CanAssign<boolean, { a: string }>)
assertType.isTrue(true as CanAssign<{ a:string, b:number }, { a: string }>)
canAssign<T>(): (subject) => true
:
✔️ immediate
, logical
Returns a compile-time validating function to ensure subject
is assignable to T
.
const isConfig = canAssign<{ a: string }>()
assertType.isTrue(isConfig({ a: 'a' }))
canAssign<T>(false): (subject) => false
:
✔️ immediate
, logical
Returns a compile-time validating function to ensure subject
is not assignable to T
.
const notA = canAssign<{ a: string }>(false)
assertType.isTrue(notA({ a: 1 }))
notA({ a: '' }) // TypeScript complains
IsNever<T>
:
✔️ logical
Check if A
is never
.
IsNever<never> // true
IsNever<1> // false
type-plus privides type checking utilities for every type.
Each type has at least 4 type checks.
Using string
as an example, there are StringType<T>
, IsString<T>
, NotStringType<T>
, and IsNotString<T>
.
Some types will have more checks, such as boolean
has StrictBooleanType<T>
, TrueType<T>
, FalseType<T>
.
You can learn more in their respective sections:
- any
- array
- bigint
- boolean
- function
- never
- null
- number
- object
- string
- symbol
- tuple
- undefined
- unknown
- void
type-plus
also provides additional type utilities.
These utilities include utility types and type-adjusted functions.
Note that most predicate
types (such as IsAny<>
) have a Then
and Else
that you can override.
e.g.:
type Yes = IsAny<any, 'yes', 'no'> // 'yes'
type No = IsAny<1, 'yes', 'no'> // 'no'
AnyType<T, Then = T, Else = never>
:T === any
.IsAny<T, Then = true, Else = false>
:T === any
(origin: ts-essentials).
At<A, N, Fail = never>
: gets the element type at indexN
in the arrayA
.CommonPropKeys<A>
: gets common keys inside the records in the arrayA
(deprecateCommonKeys
).Concat<A, B>
:[...A, ...B]
.CreateTuple<L, T>
: CreatesTuple<T>
withL
number of elements.drop(array, value)
: drop a particular value from an array.DropFirst<A>
: drops the first value type ofA
.DropLast<A>
: drops the last value type ofA
.DropMatch<A, Criteria>
: drops entries matchingCriteria
in array or tupleA
.DropUndefined<A>
: drop undefined entries from array of tupleA
.Filter<A, Criteria>
: filter the array or tupleA
, keeping entries satisfyingCriteria
. Deprecated. Renaming toKeepMatch
FindFirst<A, Criteria>
: gets the first type satisfyingCriteria
.FindLast<A, Criteria>
: gets the last type satisfyingCriteria
.Head<A>
: gets the first entry in the array.IntersectOfProps<A, K>
: gets the intersect ofA[K]
types (deprecateMapToProp
)IsArray<T>
:logical
predicate forArray
.KeepMatch<A, Criteria>
: keeps entries satisfyingCriteria
in array or tupleA
.Last<A>
: gets the last type of array or tuple.literalArray(...entries)
: return an array whose items are restricted to the provided literals.PadLeft<A, Total, PadWith>
: padsA
withPadWith
if the length ofA
is less thanL
.reduceWhile()
:reduce()
with predicate for early termination.
A simple version of the same function in theramda
package.Reverse<A>
: reverses the order ofA
.Some<A, Criteria>
: true if some elements inA
matchesCriteria
.Tail<A>
: Gets the types of a tuple except the first entry.UnionOfProps<A, K>
: gets the union ofA[K]
types (deprecatePropUnion
).UnionOfValues<A>
: gets the union of value types inA
(deprecateArrayValue
).ArrayPlus.IndexAt<A, N, Fail = never>
: gets the normalized index forA
.ArrayPlus.IsIndexOutOfBound<A, N, Then = true, Else = false>
: IsN
an out of bound index ofA
. Supports negative numbers.ArrayType<T, Then = N, Else = never>
: Is the typeT
exactly an array and not a tuple.
KeyTypes
: type of all keys.PrimitiveTypes
: all primitive types, includingFunction
,symbol
, andbigint
.ComposableTypes
: Types that can contain custom properties. i.e.object
,array
,function
.NonComposableTypes
: Types that cannot contain custom properties. i.e. not composable.
JSONPrimitive
: primitive types valid in JSONJSONObject
: JSON objectJSONArray
: JSON arrayJSONTypes
: all JSON compatible types.JSONTypes.get<T>(obj, ...props)
: get a cast value in JSON
import { JSONTypes } from 'type-plus'
const someJson: JSONTypes = { a: { b: ['z', { c: 'miku' }]}}
JSONTypes.get<string>(someJson, 'a', 'b', 1, 'c') // miku
filterKey()
: type adjusted filter by key.findKey()
: type adjusted find by key.forEachKey()
: type adjusted for each by key.HasKey<T, K>
: predicate type checkingT
has keyK
.hasKey()
: function ofHasKey
.IsRecord<T>
:logical
predicate forRecord
.KeysWithDiffTypes<A, B>
: gets the keys common inA
andB
but with different value type.mapKey()
: type adjusted map by key.RecordValue<R>
: gets the value typeT
fromRecord<any, T>
video.reduceByKey()
: type adjusted reduce by key.someKey()
: type adjusted some by key.SpreadRecord<A, B>
: type for{...a, ...b}
when botha
andb
areRecord
for array, just do[...A, ...B]
.
AwaitedProp<T, V>
:Awaited
on specified propsP
inT
.isPromise<R>(subject: any)
:isPromise()
type guard.MaybePromise<T>
: Alias ofT | Promise<T>
.PromiseValue<P>
: Gets the type within the Promise.PromiseValueMerge<P1, P2, ...P9>
: Merge the values of multiple promises.mapSeries()
: Similar tobluebird.mapSeries()
but works withasync
/await
.transformMaybePromise(value, transformer)
: Apply thetransformer
to thevalue
.
It is also exported underMaybePromise.transform()
.
ANotB<A, B>
: get object with properties inA
and not inB
, including properties with a different value type.BNotA<A, B>
: flip ofANotB
as<T>(subject)
: assertsubject
asT
. Avoid ASI issues such as;(x as T).abc
asAny(subject)
: assertsubject
asany
. Avoid ASI issue such as;(x as any).abc
EitherAnd<A, B, [C, D]>
: combines 2 to 4 types asA | B | (A & B)
. This is useful for combining options. Deprecated. Renamed toEitherOrBoth
.EitherOrBoth<A, B, [C, D]>
: combines 2 to 4 types asA | B | (A & B)
. This is useful for combining options video.Except<T, K>
: Deprecated. Same asOmit<T, K>
.ExcludePropType<T, U>
: excludes typeU
from properties inT
.KeyofOptional<T>
:keyof
that works withRecord<any, any> | undefined
.KnownKeys<T>
: extract known (defined) keys from typeT
.LeftJoin<A, B>
: left joinA
withB
NonNull<T>
: removenull
NonUndefined<T>
: removeundefined
Omit<T, K>
: FromT
, pick a set of properties whose keys are not in the unionK
. This is the opposite ofPick<T, K>
.OptionalKeys<T>
: gets keys of optional properties inT
.PartialExcept<T, U>
: Deprecated. Same asPartialOmit<T, U>
.PartialOmit<T, U>
: makes the properties not specified inU
becomes optional.PartialPick<T, U>
: makes the properties specified inU
becomes optional.Pick<T, K>
: pick propertiesK
fromT
. Works with unions.RecursivePartial<T>
: make typeT
optional recursively.RecursiveRequired<T>
: make typeT
required recursively.ReplaceProperty<T, K, V>
: replace propertyK
inT
withV
.RequiredKeys<T>
: gets keys of required properties inT
.RequiredPick<T, U>
: makes the properties specified inU
become required.RequiredExcept<T, U>
: makes the properties not specified inU
become required.RecursiveIntersect<T, U>
: intersect typeU
ontoT
recursively.ValueOf<T>
: type of the value of the properties ofT
.Widen<T>
: widen literal types.- PropType: ...no helper type for this. Just do
YourType['propName']
.
Type predicates are type alias that returns true
or false
.
They can be used to compose complex types.
HasKey<T, K>
: predicate type checkingT
has keyK
.IsAny<T>
:T === any
(updated to impl: expect-type).IsBoolean<T>
: check forboolean
, but not fortrue
norfalse
.IsDisjoint<A, B>
: isA
andB
is a disjoint set.IsEmptyObject<T>
: isT === {}
.IsLiteral<T>
: isT
a literal type (literal string or number).
If<Condition, Then = true, Else = false>
: if statement.And<A, B, Then = true, Else = false>
: logicalAND
.Or<A, B, Then = true, Else = false>
: logicalOR
.Xor<A, B, Then = true, Else = false>
: logicalXOR
.Not<X, Then = true, Else = false>
: logicalNOT
.
Note that these types work correctly with the boolean
type.
e.g.:
And<boolean, true> -> boolean
Not<boolean> -> boolean
There is a problem with generic distribution: microsoft/TypeScript#41053 So you may encounter some weird behavior if your logic is complex.
Numeric
: eithernumber
orbigint
(origin: type-fest).Zero
:0
innumber
orbigint
(origin: type-fest).Integer<N, Then = N, Else = never>
: is integer (origin: type-fest).IsInteger<N, Then = true, Else = false>
: is integer.IsWhole<N, Then = true, Else = false>
: is integer.Negative<N, Then = N, Else = never>
: is negative (origin: type-fest).NonNegative<N, Then = N, Else = never>
: is non-negative (origin: type-fest)NumberType<T, Then = N, Else = never>
: Is the typeT
exactlynumber
.Positive<N, Then = N, Else = never>
: is positive.IsPositive<N, Then = N, Else = never>
: is positive.
Abs<N, Fail=never>
:Abs(N)
,Abs<number>
returnsFail
.Max<A, B, Fail=never>
:max(A, B)
, for whole number,Fail
otherwise.GreaterThan<A, B, Fail=never>
:A > B
for whole numbers,Fail
otherwise.
Add<A, B, Fail=never>
:A + B
for positive and whole numbers,Fail
otherwise.Subtract<A, B, Fail=never>
:A - B
for positive and whole numbers,Fail
otherwise.Increment<A, Fail=never>
: alias ofAdd<A, 1, Fail>
.Decrement<A, Fail=never>
: alias ofSubtract<A, 1, Fail>
.
amend(subject)...
: amend subject as union or intersect ofT
.facade(subject, ...props)
: create a facade ofsubject
.getField(subject, key, defaultValue)
: get a field from a subject. Works against nullable and optional subject.hasKey()
: function ofHasKey
.hasProperty(value, prop)
: assertvalue
has propertyprop
. This will pick the correct union type.isConstructor(subject)
: type guardsubject
is a constructor.isSystemError(code, err)
: type guarderr
with NodeJS error code.omit(obj, ...props)
: omit properties fromobj
.pick(obj, ...props)
: pick properties fromobj
.record<K, V>(value?)
: create aRecord<K, V>
without extra object prototype.record<R>(value?)
: create a recordR
(e.g.{ a: number }
) without extra object prototype.required(...)
: merge options and removePartial<T>
. Fromunpartial
requiredDeep(...)
: merge options deeply and removePartial<T>
. Fromunpartial
split(target, ...splitters)
: split one object into multiple objects.stub<T>(value)
: stub a particular typeT
.stub.build<T>(init?)
: build a stub for particular typeT
.typeOverrideIncompatible<T>()
: override only the incompatible portion between two types.unpartial()
: merge options and removePartial<T>
values. Fromunpartial
type A = {
foo: boolean,
bar: string,
baz: string
}
const overrider = typeOverrideIncompatible<A>()
const source = {
foo: 1,
bar: 'bar',
baz: 'baz'
}
// only the `foo` property is available to override.
overrider(source, { foo: !!source.foo })
The TypeScript type system is structural.
In some cases, we want to express a type with nominal behavior.
type-plus
provides two kinds of nominal types: Brand
and Flavor
.
Brand<B, T>
:
brand(type, subject?)
:
Branded nominal type is the stronger nominal type of the two. It disallows unbranded type assigned to it:
const a = brand('a', { a: 1 })
const b = { a: 1 }
a = b // error
subject
can be any type, from primitive to strings to objects.
brand(type)
:
If you do not provide subject
, brand(type)
will return a brand creator,
so that you can use it to create multiple branded values:
const nike = brand('nike')
const shirt = nike('shirt')
const socks = nike('socks')
Flavor<F, T>
:
flavor(type, subject?)
:
The key difference between Flavor
and Brand
is that
unflavored type can be assigned to Flavor
:
let f = flavor('orange', 'soda')
f = 'mist' // ok
Also, Brand
of the same name can be assigned to Flavor
,
but Flavor
of the same name cannot be assigned to Brand
.
nominalMatch(a, b)
:
nominalMatch()
can be used to compare Brand
or Flavor
.
const b1 = brand('x', 1)
const b2 = brand('y', 1)
nominalMatch(b1, b2) // false
AnyFunction<P, R>
: a generic type for any functionExtractFunction<F>
: extract the function signature from a typeF
.extractFunction(fn: F)
: adjust type offn
to its function signature only.inspect<T>(value: T, inspector?: (v: T) => void)
: inspect a value and return it.
Inspector defaults toconsole.dir()
ChainFn<T>: T
: chain function that returns the input type.compose(...fns): F
: compose functions
context()
: a context builder. This is useful to build context for functional programming.
It is a sync version of theAsyncContext
from async-fp.
import { context } from 'type-plus'
// { a: 1, b: 2 }
const ctx = context({ a: 1 })
.extend(c => ({ b: c.a + 1 }))
.build()
Some code in this library is created by other people in the TypeScript community. I'm merely adding them in and maybe making some adjustments. Whenever possible, I add attribution to the person who created those codes in the file.
- expect-type: Compile-time tests for types
- hotscript: Higher-order TypeScript
- spec.ts: write tests for your types!
- ts-essentials: all essential TypeScript types in one place.
- ts-expect: Checks values in TypeScript match expectations.
- ts-toolbelt: TypeScript's largest utility library.
- type-fest: a collection of essential TypeScript types.
- type-zoo: a modest type lib usable today.
- typepark: a new type collection offering tuple manipulation and
Pipe
. - typelevel-ts: a type lib by @gcanti, author of several FP libraries in TS.
- typical: a playground of type-level operations for TypeScript.
- utility-types: collection of utility types, complementing TypeScript build-in mapped types ans aliases.
# after fork and clone
npm install
# begin making changes
git checkout -b <branch>
npm run watch
# after making change(s)
git commit -m "<commit message>"
git push
# create PR
This repository contributors are welcome to use Wallaby.js OSS License to get test results immediately as you type, and see the results in your editor right next to your code.