ts-type-checked
supports (reasonably large but still only) a subset of TypeScript features:
Type / feature | Support | Notes | Implementation |
---|---|---|---|
bigint ,boolean ,number ,string ,symbol
|
✓ | Primitive types that can be checked using the typeof operator |
typeof value === 'bigint' ,typeof value === 'boolean' ,typeof value === 'number' ,typeof value === 'string' ,typeof value === 'symbol'
|
BigInt ,Boolean ,Number ,String ,Symbol
|
✓ | Boxed types are converted to their un-boxed versions and checked accordingly |
typeof value === 'bigint' ,typeof value === 'boolean' ,typeof value === 'number' ,typeof value === 'string' ,typeof value === 'symbol'
|
object
|
✓ | All non-primitives (See description of the object type here) |
typeof value === 'function' || (typeof value === 'object' && value !== null)
|
Object
|
✓ |
All objects that inherit from Object (i.e. everything except for null and undefined ) and implement the Object interface.
Check the definition of the Object interface here here
|
value !== null && value !== undefined && typeof value.toString === 'function' && ...
|
Date
|
✓ | Date objects |
value instanceof Date
|
Set<T>
|
✓ |
ES6 Sets
Array.from and Array.every methods are used so they need to be available.
|
(value instanceof Set) && Array.from(value.values()).every(value => isA<T>(value))
|
Map<K, V>
|
✓ |
ES6 Maps
Array.from and Array.every methods are used so they need to be available.
|
(value instanceof Map) && Array.from(value.entries()).every(entry => isA<K>(entry[0]) && isA<V>(entry[1]))
|
interface T { ,
type T = {
|
✓ |
All objects that inherit from Object (i.e. everything except for null and undefined ) and have all the properties of interface T .
Recursive types are also supported. |
value !== null && value !== undefined && typeof value.name === 'string' && typeof value.age === 'number' && isA<T[]>(value.others)
|
class A
|
✓ | Classes |
Classes are checked according to the duck test: If it walks like a duck and it quacks like a duck, then it must be a duck. That also means that private and protected members are not checked.
See interfaces
|
Record<string, T> ,
Record<number, T> ,
{
{
{
|
~ |
All non-primitives (See description of the object type here) whose properties are assignable to T
Object.keys and Array.every methods are used to check the properties so they need to be available
The support for numeric indexes works something like this: if a key can be casted to a number (not a NaN ) OR is equal to "NaN" then the value is checked against the index type.
|
(typeof value === 'function' || (typeof value === 'object' && value !== null)) && Object.keys(value).every(key => isA<T>(value[key]))
Or for numeric indexes: (typeof value === 'function' || (typeof value === 'object' && value !== null)) && Object.keys(value).every(key => (isNaN(parseFloat(key)) && key !== 'NaN') || isA<T>(value[key]))
|
'primary' ,21 ,true ,false ,null ,undefined
|
✓ | Literal types |
value === 'primary' ,value === 21 ,value === true ,value === false ,value === null ,value === undefined , |
A | B
|
✓ | Union types |
isA<A>(value) || isA<B>(value)
|
A & B
|
✓ | Intersection types |
isA<A>(value) && isA<B>(value)
|
Type<T>
|
✓ |
Generic types are supported provided all the type arguments (T in this case) are specified.What this means is that you cannot create a generic function and use the isA to check whether a value is assignable to a type parameter of that function.
Conditional types are also supported as part of generic types. |
|
T[] ,Array<T> ,ReadonlyArray<T> , |
✓ |
Array types are checked using Array.isArray , the types of elements using Array.every so these need to be available
|
Array.isArray(value) && value.every(element => isA<T>(element))
|
[T, U, V]
|
✓ | Tuple types are checked for their length as well as the types of their members |
Array.isArray(value) && value.length === 3 && isA<T>(value[0]) && isA<U>(value[1]) && isA<V>(value[2])
|
Function ,(...args: any[]) => any ,new () => {}
|
~ | The signature of the function cannot be checked (since you cannot check the return type of a function without calling it) |
typeof value === 'function'
|
interface T {
|
~ | The signature of the function cannot be checked (since you cannot check the return type of a function without calling it) |
typeof value === 'function' && typeof(value.callCount) === 'number'
|
Promise<T>
|
~ |
The resolution value of the promise cannot be checked.
Checking for promise types is discouraged in favor of using the Promise.resolve method.
|
!!value && typeof value.then === 'function' && typeof value.catch === 'function'
|
RegExp
|
✓ | RegExp objects |
value instanceof RegExp
|
Node ,Element ,HTMLElement ,HTMLDivElement
|
✓ | The global DOM classes |
value instanceof Node ,value instanceof Element ,value instanceof HTMLElement ,value instanceof HTMLDivElement
|
any ,unknown
|
✓ | Checks for these will always be true |
true
|
never
|
✓ |
Checks for never will always be false
|
false
|
- Promise resolution values It is impossible to check what the value of a resolved promise will be
- Function return types and signatures It is impossible to check anything about a function apart from the fact that it is a function
When strictNullChecks
is disabled in you tsconfig.json
the value of ts-type-checked
decreases massively - if e.g. any/all properties of an object can be null
or undefined
, there is no difference between let's say a string 'hello world'
and an object of type MyInterface
:
interface MyInterface {
myProperty: string;
}
Why? Because string.myProperty
is undefined
! The moral of the story: turn strictNullChecks
on, it's good for your code!