type-plus




Provides additional types and type adjusted utilities for TypeScript.
Feature Highlights
Installation
npm install type-plus
// or
yarn add type-plus
Runtime type checker
Bringing the power of TypeScript to JavaScript runtime.
const eslintConfig = T.object.create({
env: O.object.create({
es6: O.boolean
}),
parseOptions: O.object.create({
ecmaVersion: O.number.list(3, 5, 6, 7, 8, 9, 10, 11, 12),
sourceType: O.string.list('script', 'module'),
ecmaFeatures: O.object.create({
globalReturn: O.boolean,
impliedStrict: O.boolean,
jsx: O.boolean
})
}),
...
})
const config: unknown = require('.eslintrc.json')
if (!T.satisfy(eslintConfig, config)) {
console.error(T.satify.getReport())
}
config.parseOptions?.ecmaVersion
All type checker functionalities are exposed as types
(alias to T
).
In addition, O
and R
are exposed to make it easier to access optional types and required types respectively.
Types supported: any
, array
, boolean
, null
, number
, object
, record
, string
, symbol
, tuple
, undefined
, union
, unknown
.
i.e., most of the basic types are supported except bigint
.
It is left out for backward compatibility reasons.
You can use one of the three functions to perform type check:
satisfy(type, subject)
:
A loose type check that permits extra elements in Tuple
and properties in Object
.
conform(type, subject)
:
A strick type check that does not allow extra elements in Tuple
and properties in Object
.
check(options, type, subject)
:
A general form of satisfy()
and conform()
.
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 returns true
or false
type to be used in type level programming.
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 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
allows 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: any = 1
assertType<boolean>(s, s => typeof s === 'boolean')
The message beautification is provided by tersify
.
assertType.isUndefiend(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)
They accepts any
and will be narrowed to the specific type.
const s: any = undefined
assertType.isUndefined(s)
s
assertType.noUndefiend(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 againsts unions.
const s: number | undefined = 1
assertType.noUndefined(s)
They accepts subject
with type any
or unknown
,
assertion will happens 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)
:
✔️ type guard
, runtime
These overloads of isType
allows you to specify a validator
.
With these overloads, subject
can be unknown
or any
.
Note that the Class
overload does not work correctly with unions
.
For more details, please check out: https://github.com/microsoft/TypeScript/issues/41050
Equal<A, B>
:
✔️ logical
Check if A
and B
are the same.
NotEqual<A, B>
:
✔️ logical
Check if A
and B
are not the same.
CanAssign<A, B>
:
✔️ logical
Check if A
can be assigned to B
.
Nominal Type
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
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'
Also, Brand
of the same name can be assigned to Flavor
,
but Flavor
of the same naem 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)
Type Utilities
type-plus
also provides additional type utilities.
These utilities includes utiltiy types and type adjusted functions.
Array function
literalArray(...entries)
: return an array those items are restricted to the provided literals.reduceWhile()
: reduce()
with predicate for early termination.
A simple version of the same function in the ramda
package.
Constant Types
JSONTypes
: all JSON compatible types.KeyTypes
: type of all keys.PrimitiveTypes
: all primitive types, including Function
, symbol
, and bigint
.
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 checking T
has key K
.hasKey()
: function of HasKey
.KeysWithDiffTypes<A, B>
: gets the keys common in A
and B
but with differnt value type.mapKey()
: type adjusted map by key.reduceKey()
: type adjusted reduce by key.someKey()
: type adjusted some by key.
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 to bluebird.mapSeries()
but works with async
/await
.
Type manipulation
ANotB<A, B>
: get object with properties in A
and not in B
, including properties with differnt value type.BNotA<A, B>
: flip of ANotB
Except<T, K>
: Deprecated. Same as Omit<T, K>
.ExcludePropType<T, U>
: excludes type U
from properties in T
.KeyofOptional<T>
: keyof
that works with Record<any, any> | undefined
.KnownKeys<T>
: extract known (defined) keys from type T
.LeftJoin<A, B>
: left join A
with B
Omit<T, K>
: From T
, pick a set of properties whose keys are not in the union K
. This is the opposite of Pick<T, K>
.PartialExcept<T, U>
: Deprecated. Same as PartialOmit<T, U>
.PartialOmit<T, U>
: makes the properties not specified in U
becomes optional.PartialPick<T, U>
: makes the properties specified in U
becomes optional.RecursivePartial<T>
: make type T
optional recursively.RecursiveRequired<T>
: make type T
required recursively.ReplaceProperty<T, K, V>
: replace property K
in T
with V
.RequiredPick<T, U>
: makes the properties specified in U
becomes required.RequiredExcept<T, U>
: makes the properties not specified in U
becomes required.RecursiveIntersect<T, U>
: intersect type U
onto T
recursively.ValueOf<T>
: type of the value of the properties of T
.- 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 checking T
has key K
.IsDisjoint<A, B>
: is A
and B
is a disjoint set.
Logical
And<A, B>
: logical AND
.Or<A, B>
: logical OR
.Xor<A, B>
: logical XOR
.Not<X>
: logical NOT
.
Note that these types work correctly with 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.
Utility Functions
facade(subject, ...props)
: create a facade of subject
.getField(subject, key, defaultValue)
: get a field from a subject. Works against nullable and optional subject.hasKey()
: function of HasKey
.hasProperty(value, prop)
: assert value
has property prop
. This will pick the correct union type.isConstructor(subject)
: type guard subject
is a constructor.pick(obj, ...props)
: pick properties from obj
.omit(obj, ...props)
: omit properties from obj
.required(...)
: merge options and removing Partial<T>
. From unpartial
requiredDeep(...)
: merge options deeply and removing Partial<T>
. From unpartial
typeOverrideIncompatible<T>()
: override only the incompatiable portion between two types.
type A = {
foo: boolean,
bar: string,
baz: string
}
const overrider = typeOverrideIncompatible<A>()
const source = {
foo: 1,
bar: 'bar',
baz: 'baz'
}
overrider(source, { foo: !!source.foo })
Attribution
Some of the code in this library are created by other people in the TypeScript community.
I merely adding them in and may be making some adjustments.
When ever possible, I add attribution to the person who created those code in the file.
Contribute
npm install
git checkout -b <branch>
npm run watch
git commit -m "<commit message>"
git push