README
type-plus
Provides additional types and type adjusted utilities for TypeScript.
Feature Highlights
Installation
npm install type-plus
// or
yarn add type-plus
Type Assertion
Besides the runtime type checker,
type-plus
also provides a few other ways to do type assertions.
There are actually at least 5 kinds of type assertions:
runtime
: validates during runtime.immediate
: validates at compile time.type guard
: User-defined type guard functions (if (isBool(s))
) introduced in TypeScript 1.6.assertion function
: assertion functions (assertIsBool(a)
) introduced in TypeScript 3.7.logical
: functions or generic types that returnstrue
orfalse
type to be used in type level programming.filter
: generic types that returnsnever
if the test fails.
Here are the type assertions provided in type-plus
.
Use the one that fits your specific needs.
assertType<T>(subject)
:
✔️ immediate
It ensures subject
satisfies T
.
It is similar to const x: T = subject
without introducing an unused variable.
You need to specify T
for it to work.
assertType<T>(subject, validator)
:
assertType<T>(subject, Class)
:
✔️ assertion function
, runtime
These overloads of assertType
allow you to specify a validator
.
With these overloads, subject
can be unknown
or any
.
If subject
fails the assertion,
a standard TypeError
will be thrown and provide better error info.
For example:
const s: unknown = 1
// TypeError: subject fails to satisfy s => typeof s === 'boolean'
assertType<boolean>(s, s => typeof s === 'boolean')
The message beautification is provided by tersify
.
assertType.isUndefined(subject)
:
assertType.isNull(subject)
:
assertType.isNumber(subject)
:
assertType.isBoolean(subject)
:
assertType.isTrue(subject)
:
assertType.isFalse(subject)
:
assertType.isString(subject)
:
assertType.isFunction(subject)
:
assertType.isConstructor(subject)
:
assertType.isError(subject)
:
✔️ immediate
, assertion function
, runtime
Compiler and runtime assertion with type narrowing from any
.
They assert the type of subject
is that specific type.
i.e. union type will fail at type level:
const s: number | undefined = undefined
assertType.isUndefined(s) // TypeScript complains
They accept any
and will be narrowed to the specific type.
const s: any = undefined
assertType.isUndefined(s)
s // type is undefined
assertType.isNever(subject)
:
✔️ immediate
Check if the subject type is never
.
This function is not very useful in actual code as TypeScript will indicate the error.
But it can be useful when writing tests for types.
This is useful for variables. For type level only check, do the following:
assertType.isTrue(true as Equal<YourType, never>)
assertType.noUndefined(subject)
:
assertType.noNull(subject)
:
assertType.noNumber(subject)
:
assertType.noBoolean(subject)
:
assertType.noTrue(subject)
:
assertType.noFalse(subject)
:
assertType.noString(subject)
:
assertType.noFunction(subject)
:
assertType.noError(subject)
:
✔️ immediate
, runtime
Compiler and runtime assertion.
Assert subject
type does not contain the specific type.
Work against unions.
const s: number | undefined = 1
assertType.noUndefined(s) // TypeScript complains
They accept subject
with type any
or unknown
,
the assertion will happen in runtime to ensure subject
is the specific type.
isType<T>(subject: T)
:
✔️ immediate
It ensures subject
satisfies T
.
It is identical to assertType<T>(subject: T)
.
You need to specify T
.
isType<T>(subject, validator)
:
isType<T>(subject, Class)
:
isType.t<T>(subject?: T)
:
✔️ immediate
, runtime
It can used as type check: isType.t<Equal<A, B>>()
,
or value type check: isType.t(valueTypeIsTrue)
.
It returns true
when passes (which is the only case when used in TypeScript).
isType.f<T>(subject?: T)
:
✔️ immediate
, runtime
It can used as type check: isType.f<Equal<A, B>>()
,
or value type check: isType.f(valueTypeIsFalse)
.
It returns true
when passes (which is the only case when used in TypeScript).
isType.equal<true|false, A, B>()
:
✔️ immediate
Slightly easier to use then isType.t<>()
and isType.f<>()
,
when doing type-level only equality comparison as you don't have to import Equal<>
.
✔️ type guard
, runtime
These overloads of isType
allow you to specify a validator
.
With these overloads, subject
can be unknown
or any
.
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
Nominal Type
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
Functional Types
ChainFn<T>: T
: chain function that returns the input type.
Type Utilities
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'
Array function
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.DropFirst<A>
: drops the first value type ofA
.DropLast<A>
: drops the last value type ofA
.Filter<A, Criteria>
: gets the array of types satisfyingCriteria
inA
.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
.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 remaining entries in the array except the first.UnionOfProps<A, K>
: gets the union ofA[K]
types (deprecatePropUnion
).UnionOfValues<A>
: gets the union of value types inA
(deprecateArrayValue
).
Constant Types
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.
JSON Support
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
Object Key functions
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.reduceKey()
: 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]
.
Promise function
isPromise<R>(subject: any)
:isPromise()
type guard.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
.
Type manipulation
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 any).abc
asAny(subject)
: assertsubject
asany
. Avoid ASI issue such as;(x as any).abc
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
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
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
.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).
Logical
If<Condition, Then = true, Else = false>
: if statement.And<A, B>
: logicalAND
.Or<A, B>
: logicalOR
.Xor<A, B>
: logicalXOR
.Not<X>
: 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: https://github.com/microsoft/TypeScript/issues/41053 So you may encounter some weird behavior if your logic is complex.
Math
Abs<N, Fail=never>
:Abs(N)
,Abs<number>
returnsFail
.IsPositive<N>
: isN
a positive number literal.IsPositive<number>
returnsfalse
.IsWhole<N>
: isN
a whole number literal.IsWhole<number>
returnsfalse
.Max<A, B, Fail=never>
:max(A, B)
, for positive and whole number,Fail
otherwise.GreaterThan<A, B, Fail=never>
:A > B
for positive and whole numbers,Fail
otherwise.
Arithmetics
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>
.
Utility Functions
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.pick(obj, ...props)
: pick properties fromobj
.omit(obj, ...props)
: omit properties fromobj
.required(...)
: merge options and removingPartial<T>
. Fromunpartial
requiredDeep(...)
: merge options deeply and removingPartial<T>
. Fromunpartial
typeOverrideIncompatible<T>()
: override only the incompatible portion between two types.
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 })
Attribution
Some of the code in this library is created by other people in the TypeScript community. I merely adding them in and maybe making some adjustments. Whenever possible, I add attribution to the person who created those codes in the file.
Similar projects
ts-essentials
, all essential TypeScript types in one place.ts-toolbelt
, a more mature type lib.type-fest
, a collection of essential TypeScript types.type-zoo
, a modest type lib usable today.typepark
, a new type collection offering tuple manipulation andPipe
.typelevel-ts
, a type lib by @gcanti, author of several FP libs in TS.typical
, a playground of type-level operations for TypeScript.
Contribute
# 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