A collection of a few small lightweight typesafe utilities.
Sometimes you will encounter situations were the types will not match what you expect from a function. This means you need to explicitly specify a type by yourself to gain the full power of TypeScript.
In this collection you will find some useful functions that are fully typed.
$ npm install --save-dev typesafe-utils
-
- is
- isTruthy
- isUndefined
- isNull
- boolean
- number
- string
- array
- object
- duplicates
- logical operators
- typeguards
| * not automatically 100% typesafe. It's better than nothing but to be 100% typesafe you need to pass generics yourself.
A bunch of utilities that return true or false. Useful for array filter functions.
Motivation: When you filter an Array, the return type is not always what you expect. Typescript will tell you the result of a filter function is the exact type you pass to the filter function. But that is not always true. If you filter out falsy values, the return type should should not contain.
// BASIC example --------------------------------------------------------------
const result = [true, false].filter(bool => !!bool)
// => result: boolean[] => [true]
import { isTruthy } from 'typesafe-utils'
const typesafeResult = [true, false].filter(isTruthy)
// => typesafeResult: true[] => [true]
// ADVANCED example -----------------------------------------------------------
const result = ['text', null, 'another text', undefined].filter(value => value !== '')
// => result: (string | null | undefined)[] => ['text', 'another text']
import { isNotEmpty } from 'typesafe-utils'
const typesafeResult = ['text', null, 'another text', undefined].filter(isNotEmpty)
// => typesafeResult: string[] => ['text', 'another text']
returns true
iff value is equals to the property you pass to the function
import { is } from 'typesafe-utils'
const result = [1, 15, 10, 43].filter(is(10))
// result: number[] => [10]
returns true
iff value is not equal to the property you pass to the function
import { isNot } from 'typesafe-utils'
const result = ['text', 'forbidden', 'blabla'].filter(isNot('forbidden'))
// result: string[] => ['text', 'blabla']
returns true
iff the attribute of the object equals the property you pass to the function
import { isProperty } from 'typesafe-utils'
type Product = {
id: number
}
const items: Product[] = [
{ id: 1 },
{ id: 3 }
]
const result = items.filter(isProperty('id', 3))
// result: Product[] => [{ id: 3 }]
returns true
iff the attribute of the object is not equal to the property you pass to the function
import { isPropertyNot } from 'typesafe-utils'
type Product = {
id: number
}
const items: Product[] = [
{ id: 156 },
{ id: 123 }
]
const result = items.filter(isPropertyNot('id', 123))
// result: Product[] => [{ id: 156 }]
returns true
iff value is not false | '' | 0 | null | undefined
import { isTruthy } from 'typesafe-utils'
const result = [true, false, undefined, null].filter(isTruthy)
// result: true[] => [true]
returns true
iff value is false | '' | 0 | null | undefined
import { isFalsy } from 'typesafe-utils'
const result = [true, false, 'text', 123, null].filter(isFalsy)
// result: (false | null)[] => [false, null]
returns true
iff the attribute of the object is truthy
import { isPropertyTruthy } from 'typesafe-utils'
type Product = {
id: number
}
const items: Product[] = [
{ id: 1 },
{ id: null },
{ id: undefined }
]
const result = items.filter(isPropertyTruthy('id'))
// result: Product[] => [{ id: 1 }]
returns true
iff the attribute of the object is falsy
import { isPropertyFalsy } from 'typesafe-utils'
type Product = {
id: number
}
const items: Product[] = [
{ id: 5 },
{ id: null }
]
const result = items.filter(isPropertyFalsy('id'))
// result: Product[] => [{ id: null }]
returns true
iff all attributes of the object are truthy
import { arePropertiesTruthy } from 'typesafe-utils'
type Product = {
id: number
name: string
}
const items: Product[] = [ ... ]
const result = items.filter(arePropertiesTruthy('id', 'name'))
returns true
iff all attributes of the object are falsy
import { arePropertiesFalsy } from 'typesafe-utils'
type Product = {
id: number
name: string
}
const items: Product[] = [ ... ]
const result = items.filter(arePropertiesFalsy('id', 'name'))
returns true
iff value is undefined
import { isUndefined } from 'typesafe-utils'
const result = [undefined, null, true].filter(isUndefined)
// result: undefined[] => [undefined]
returns true
iff value is not undefined
import { isNotUndefined } from 'typesafe-utils'
const result = [null, undefined].filter(isNotUndefined)
// result: null[] => [null]
returns true
iff the attribute of the object is undefined
import { isPropertyUndefined } from 'typesafe-utils'
type Product = {
id: number | undefined
}
const items: Product[] = [
{ id: 1 },
{ id: undefined }
]
const result = items.filter(isPropertyUndefined('id'))
// result: Product[] => [{ id: undefined }]
returns true
iff the attribute of the object is not undefined
import { isPropertyNotUndefined } from 'typesafe-utils'
type Product = {
id: number
}
const items: Product[] = [
{ id: 5 },
{ id: undefined }
]
const result = items.filter(isPropertyNotUndefined('id'))
// result: Product[] => [{ id: 5 }]
returns true
iff all attributes of the object are undefined
import { arePropertiesUndefined } from 'typesafe-utils'
type Product = {
id: number
name: string
}
const items: Product[] = [ ... ]
const result = items.filter(arePropertiesUndefined('id', 'name'))
returns true
iff all attributes of the object are not undefined
import { arePropertiesNotUndefined } from 'typesafe-utils'
type Product = {
id: number
name: string
}
const items: Product[] = [ ... ]
const result = items.filter(arePropertiesNotUndefined('id', 'name'))
returns true
iff value is null
import { isNull } from 'typesafe-utils'
const result = [null, undefined].filter(isNull)
// result: null[] => [null]
returns true
iff value is not null
import { isNotNull } from 'typesafe-utils'
const result = [false, null].filter(isNotNull)
// result: boolean[] => [false]
returns true
iff the attribute of the object is null
import { isPropertyNull } from 'typesafe-utils'
type Product = {
id: number | null
}
const items: Product[] = [
{ id: 0 },
{ id: null }
]
const result = items.filter(isPropertyNull('id'))
// result: Product[] => [{ id: null }]
returns true
iff the attribute of the object is not null
import { isPropertyNotNull } from 'typesafe-utils'
type Product = {
id: number
}
const items: Product[] = [
{ id: 5 },
{ id: null }
]
const result = items.filter(isPropertyNotNull('id'))
// result: Product[] => [{ id: 5 }]
returns true
iff all attributes of the object are null
import { arePropertiesNull } from 'typesafe-utils'
type Product = {
id: number
name: string
}
const items: Product[] = [ ... ]
const result = items.filter(arePropertiesNull('id', 'name'))
returns true
iff all attributes of the object are not null
import { arePropertiesNotNull } from 'typesafe-utils'
type Product = {
id: number
name: string
}
const items: Product[] = [ ... ]
const result = items.filter(arePropertiesNotNull('id', 'name'))
returns true
iff value is of type boolean
import { isBoolean } from 'typesafe-utils'
const result = [true, 'some text', 1, false].filter(isBoolean)
// result: boolean[] => [true, false]
returns true
iff value is true
import { isTrue } from 'typesafe-utils'
const result = [true, 'some text', 1].filter(isTrue)
// result: true[] => [true]
returns true
iff value is not true
Note: it is currently not possible to make this function fully typesafe.
[true, 123].filter(isNotTrue)
will have the type(false | number)[]
import { isNotTrue } from 'typesafe-utils'
const result = [true, false].filter(isNotTrue)
// result: false[] => [false]
returns true
iff the attribute of the object is true
import { isPropertyTrue } from 'typesafe-utils'
type Product = {
available: boolean | null
}
const items: Product[] = [
{ available: true },
{ available: null }
]
const result = items.filter(isPropertyTrue('available'))
// result: Product[] => [{ available: true }]
returns true
iff the attribute of the object is not true
import { isPropertyNotTrue } from 'typesafe-utils'
type Product = {
id: number
}
const items: Product[] = [
{ available: true },
{ available: false }
]
const result = items.filter(isPropertyNotTrue('available'))
// result: Product[] => [{ available: false }]
returns true
iff all attributes of the object are true
import { arePropertiesTrue } from 'typesafe-utils'
type Product = {
count: number
available: string
}
const items: Product[] = [ ... ]
const result = items.filter(arePropertiesTrue('count', 'available'))
returns true
iff all attributes of the object are not true
import { arePropertiesNotTrue } from 'typesafe-utils'
type Product = {
count: number
available: string
}
const items: Product[] = [ ... ]
const result = items.filter(arePropertiesNotTrue('count', 'available'))
returns true
iff value is false
import { isFalse } from 'typesafe-utils'
const result = [0, false, undefined].filter(isFalse)
// result: false[] => [false]
returns true
iff value is not false
Note: it is currently not possible to make this function fully typesafe.
[false, 123].filter(isNotFalse)
will have the type(true | number)[]
import { isNotFalse } from 'typesafe-utils'
const result = [false, null].filter(isNotFalse)
// result: null[] => [null]
returns true
iff the attribute of the object is false
import { isPropertyFalse } from 'typesafe-utils'
type Product = {
available: boolean | null
}
const items: Product[] = [
{ available: false },
{ available: true },
{ available: null }
]
const result = items.filter(isPropertyFalse('available'))
// result: Product[] => [{ available: false }]
returns true
iff the attribute of the object is not false
import { isPropertyNotFalse } from 'typesafe-utils'
type Product = {
id: number
}
const items: Product[] = [
{ available: true },
{ available: false }
]
const result = items.filter(isPropertyNotFalse('available'))
// result: Product[] => [{ available: true }]
returns true
iff all attributes of the object are false
import { arePropertiesFalse } from 'typesafe-utils'
type Product = {
count: number
available: string
}
const items: Product[] = [ ... ]
const result = items.filter(arePropertiesFalse('count', 'available'))
returns true
iff all attributes of the object are not false
import { arePropertiesNotFalse } from 'typesafe-utils'
type Product = {
count: number
available: string
}
const items: Product[] = [ ... ]
const result = items.filter(arePropertiesNotFalse('count', 'available'))
returns true
iff value is of type number
import { isNumber } from 'typesafe-utils'
const result = [0, false, undefined, 5].filter(isNumber)
// result: number[] => [0, 5]
returns true
iff value is 0
import { isZero } from 'typesafe-utils'
const result = [0, false, undefined, 5].filter(isZero)
// result: 0[] => [0]
returns true
iff value is not 0
Note: it is currently not possible to make this function fully typesafe.
[0, null].filter(isNotTrue)
will have the type(number | null)[]
import { isNotZero } from 'typesafe-utils'
const result = [0, 123].filter(isNotZero)
// result: number[] => [123]
returns true
iff the attribute of the object is 0
import { isPropertyZero } from 'typesafe-utils'
type Product = {
price: number
}
const items: Product[] = [
{ price: 0 },
{ price: 4 },
{ price: 15 }
]
const result = items.filter(isPropertyZero('price'))
// result: Product[] => [{ price: 0 }]
returns true
iff the attribute of the object is not 0
import { isPropertyNotZero } from 'typesafe-utils'
type Product = {
price: number
}
const items: Product[] = [
{ price: 0 },
{ price: 12 }
]
const result = items.filter(isPropertyNotZero('price'))
// result: Product[] => [{ price: 23 }]
returns true
iff all attributes of the object are 0
import { arePropertiesZero } from 'typesafe-utils'
type Product = {
count: number
speed: string
}
const items: Product[] = [ ... ]
const result = items.filter(arePropertiesZero('count', 'speed'))
returns true
iff all attributes of the object are not 0
import { arePropertiesNotZero } from 'typesafe-utils'
type Product = {
count: number
speed: string
}
const items: Product[] = [ ... ]
const result = items.filter(arePropertiesNotZero('count', 'speed'))
returns true
iff value is of type string
import { isString } from 'typesafe-utils'
const result = ['', false, null, 'text'].filter(isString)
// result: string[] => ['', 'text]
returns true
iff value is ''
import { isEmpty } from 'typesafe-utils'
const result = ['', false, null, 'text'].filter(isEmpty)
// result: ''[] => ['']
returns true
iff value is not ''
import { isNotEmpty } from 'typesafe-utils'
const result = ['', 5].filter(isNotEmpty)
// result: number[] => [5]
returns true
iff the attribute of the object is ''
import { isPropertyEmpty } from 'typesafe-utils'
type Product = {
label: string
}
const items: Product[] = [
{ label: '' },
{ label: 'label-1' }
]
const result = items.filter(isPropertyEmpty('label'))
// result: Product[] => [{ label: '' }]
returns true
iff the attribute of the object is not ''
import { isPropertyNotEmpty } from 'typesafe-utils'
type Product = {
label: string
}
const items: Product[] = [
{ label: 'label-123' },
{ label: '' }
]
const result = items.filter(isPropertyNotEmpty('label'))
// result: Product[] => [{ label: 'label-123' }]
returns true
iff all attributes of the object are ''
import { arePropertiesEmpty } from 'typesafe-utils'
type Person = {
name: number
firstName: string
}
const items: Person[] = [ ... ]
const result = items.filter(arePropertiesEmpty('name', 'firstName'))
returns true
iff all attributes of the object are not ''
import { arePropertiesNotEmpty } from 'typesafe-utils'
type Person = {
name: number
firstName: string
}
const items: Person[] = [ ... ]
const result = items.filter(arePropertiesNotEmpty('name', 'firstName'))
returns true
iff value is of type Array
import { isArray } from 'typesafe-utils'
const result = [[], null, 123, [0, 1]].filter(isArray)
// result: number[][] => [[], [0, 1]]
returns true
iff an array contains at least one item
import { isArrayNotEmpty } from 'typesafe-utils'
const nonEmptyArray = ['hi']
if (!!nonEmptyArray.length) {
nonEmptyArray[0].toUpperCase() // ERROR: Object is possibly 'undefined'
}
if (isArrayNotEmpty(nonEmptyArray)) {
// TypeScript will know that the array contains at least 1 item, so it will not complain
nonEmptyArray[0].toUpperCase()
}
returns true
iff an array contains no items
import { isArrayEmpty } from 'typesafe-utils'
const emptyArray: string[] = []
if (isArrayEmpty(emptyArray)) {
// emptyArray does not contain any items
}
returns true
iff value is of type object
import { isObject } from 'typesafe-utils'
type SomeType = {
prop?: number
}
const now = new Date()
const result = [{}, now, null, { prop: 123 }].filter(isObject)
// result: (SomeType | Date)[] => [{}, now, { prop: 123 }]
returns true
iff value is of the primitive type object
and not derived from a class
like Date
or else.
import { isPrimitiveObject } from 'typesafe-utils'
type SomeType = {
prop?: number
}
const now = new Date()
const result = [{}, now, null, { prop: 123 }].filter(isPrimitiveObject)
// result: SomeType[] => [{}, { prop: 123 }]
Removes duplicates from an array. Only the first occurrence of an item will be kept.
import { filterDuplicates } from 'typesafe-utils'
const items = [1, 2, 3, 5, 8, 1]
const filteredItems = items.filter(filterDuplicates)
// filteredItems: number[] => [1, 2, 3, 5, 8]
Removes duplicates from an array by its key. Only the first occurrence of an item will be kept.
Motivation: It is less error-prone if you can only pass the keys an object provides to a filter function. With this function you get full types support.
import { filterDuplicatesByKey } from 'typesafe-utils'
type Product = {
id: number
name: string
}
const items: Product[] = [
{ id: 1, name: 'name-1' },
{ id: 2, name: 'name-2' },
{ id: 3, name: 'name-1' },
{ id: 4, name: 'name-2' }
]
const filteredItems = items.filter(filterDuplicatesByKey('name'))
// filteredItems: Product[] => [{ id: 1, name: 'name-1' }, { id: 2, name: 'name-2' }]
const willThrowAnError = items.filter(filterDuplicatesByKey('price'))
// throws: Argument of type '"price"' is **not** assignable to parameter of type '"id" | "name"'
Combines (&&
) multiple filter functions.
import { and, isString } from 'typesafe-utils'
const items = [null, "test", undefined, "hi"]
const isShortString = and<string, any>(isString, (value) => value.length < 3)
const filteredItems = items.filter(isShortString)
// filteredItems: string[] => ['hi']
Combines (||
) multiple filter functions.
import { or } from 'typesafe-utils'
const items = [10, 2, 3, 5, 8, 1]
const isFiveOrTen = or((value) => value === 5, (value) => value === 10)
const filteredItems = items.filter(isFiveOrTen)
// filteredItems: number[] => [10, 5]
Inverts a filter function.
import { not, filterDuplicates } from 'typesafe-utils'
type Product = {
id: number
name: string
}
const items: Product[] = [
{ id: 1, name: 'name-1' },
{ id: 2, name: 'name-2' },
{ id: 3, name: 'name-1' },
{ id: 4, name: 'name-2' }
]
const filteredItems = items.filter(not<Product>(filterDuplicatesByKey('name')))
// filteredItems: Product[] => [{ id: 3, name: 'name-1' }, { id: 4, name: 'name-2' }]
// The `not` function takes two optional type arguments.
// The first is the type you expect the filter function to return.
// The second is the Type of the Array you want to filter.
// e.g.
const notNull = [1, 5, null].filter<number, number | null>(not((value => value === null)))
// notNull: number[] => [1, 5]
Creates a typeguard filter.
import { createFilter } from 'typesafe-utils'
interface Item {
id: number
}
interface ItemWithName extends Item {
name: string
}
const items: (Item | ItemWithName | undefined)[] = [
{ id: 1 },
undefined
{ id: 3, name: 'name-1' },
{ id: 4 }
]
const filterHasName = createFilter<ItemWithName>((item) => !!item?.name)
const filteredItems = items.filter(filterHasName)
// filteredItems: ItemWithName[] => [{ id: 3, name: 'name-1' }]
sort number
in ASC order
import { sortNumberASC } from 'typesafe-utils'
const items = [4, -1, 3, 0]
const result = items.sort(sortNumberASC)
// result: number[] => [-1, 0, 3, 4]
sort number
in DESC order
import { sortNumberDESC } from 'typesafe-utils'
const items = [2, -5, 0]
const result = items.sort(sortNumberDESC)
// result: number[] => [2, 0, -5]
sort property of type number
in ASC order
import { sortNumberPropertyASC } from 'typesafe-utils'
type Car {
speed: number
}
const items: Car[] = [
{ speed: 113 },
{ speed: 100 },
{ speed: 95 }
]
const result = items.sort(sortNumberPropertyASC('speed'))
// result: Car[] => [{ speed: 95 }, { speed: 100 }, { speed: 113 }}
sort property of type number
in DESC order
import { sortNumberPropertyDESC } from 'typesafe-utils'
type Car {
speed: number
}
const items: Car[] = [
{ speed: 70 }
{ speed: 87 }
]
const result = items.sort(sortNumberPropertyDESC('speed'))
// result: Car[] => [{ speed: 87 }, { speed: 70 }]
sort string
in ASC order
import { sortStringASC } from 'typesafe-utils'
const items = ['Hi', 'apples']
const result = items.sort(sortStringASC)
// result: string[] => ['apples', Hi']
sort string
in DESC order
import { sortStringDESC } from 'typesafe-utils'
const items = ['apple', 'banana']
const result = items.sort(sortStringDESC)
// result: string[] => ['banana', 'apple']
sort property of type string
in ASC order
import { sortStringPropertyASC } from 'typesafe-utils'
type Car {
color: string
}
const items: Car[] = [
{ color: 'green' },
{ color: 'brown' }
]
const result = items.sort(sortStringPropertyASC('color'))
// result: Car[] => [{ color: 'brown' }, { color: 'green' }]
sort property of type string
in DESC order
import { sortStringPropertyDESC } from 'typesafe-utils'
type Car {
color: string
}
const items: Car[] = [
{ color: 'red' },
{ color: 'blue' }
]
const result = items.sort(sortStringPropertyDESC('color'))
// result: Car[] => [{ color: 'red' }, { color: 'blue' }]
sort Date
in ASC order
import { sortDateASC } from 'typesafe-utils'
const today = new Date()
const tomorrow = new Date(today.getTime() + 24 * 60 * 60 * 1000)
const items = [tomorrow, today]
const result = items.sort(sortDateASC)
// result: Date[] => [today, tomorrow]
sort Date
in DESC order
import { sortDateDESC } from 'typesafe-utils'
const today = new Date()
const tomorrow = new Date(today.getTime() + 24 * 60 * 60 * 1000)
const items = [today, tomorrow]
const result = items.sort(sortDateDESC)
// result: Date[] => [tomorrow, today]
sort property of type Date
in ASC order
import { sortDatePropertyASC } from 'typesafe-utils'
type Smartphone = {
releaseDate: Date
}
const today = new Date()
const tomorrow = new Date(today.getTime() + 24 * 60 * 60 * 1000)
const items: Smartphone[] = [
{ releaseDate: tomorrow },
{ releaseDate: today }
]
const items: Smartphone[] = []
const result = items.sort(sortDatePropertyASC('releaseDate'))
// result: Smartphone[]=> [{ releaseDate: today }, { releaseDate: tomorrow }]
sort property of type Date
in DESC order
import { sortDatePropertyDESC } from 'typesafe-utils'
type Smartphone = {
releaseDate: Date
}
const today = new Date()
const tomorrow = new Date(today.getTime() + 24 * 60 * 60 * 1000)
const items: Smartphone[] = [
{ releaseDate: today },
{ releaseDate: tomorrow }
]
const result = items.sort(sortDatePropertyDESC('releaseDate'))
// result: Smartphone[] => [{ releaseDate: tomorrow }, { releaseDate: today }]
Picks an attribute from an Object.
import { pick } from 'typesafe-utils'
interface Item {
id: number
name: string
price: number
}
const items: Item[] = [
{ id: 1, name: '', price: 123 },
{ id: 3, name: '', price: 0 },
{ id: 7, name: '', price: 12 },
]
const ids = items.map(pick('id'))
// ids: number[] => [ 1, 3, 7 ]
Creates a deep copy of an object containing primitive values.
Motivation: I have seen a variety of clone-functions that return any. There you would need to always specify the type by ourself. Using this function, you will get a correctly typed object back.
import { deepClone } from 'typesafe-utils'
const objectToClone: MyTypedObject = { ... }
const clonedObject = deepClone(objectToClone)
// => clonedObject: MyTypedObject => { ... }
Removes duplicates from an array.
import { uniqueArray } from 'typesafe-utils'
const unique = uniqueArray('John', 'Max', 'John')
// => unique: string[] => ['John', 'Max']
Contains all Truthy
values (everything excluding Falsy values)
import { Truthy } from 'typesafe-utils'
export const isTruthy = <T>(value: T): value is Truthy<T> => !!value
const truthy = [123, undefined].filter(isTruthy) // => number[]
const notTruthy = [false, true].filter(isTruthy) // => never[]
Contains all Falsy
values (false | '' | 0 | null | undefined)
import { Falsy } from 'typesafe-utils'
export const isFalsy = <T>(value: T): value is Falsy<T> => !value
const falsy = [undefined, ''].filter(isFalsy) // => undefined[]
const notFalsy = [0, ''].filter(isFalsy) // => never[]
Allows you to write custom TypeGuard
functions.
import { TypeGuard } from 'typesafe-utils'
type Project {
id: number
// ...
}
const isProject = <T>(value: T): value is TypeGuard<Project, T> => value?.hasOwnProperty('id')
const p1 = isProject({ id: 1 }) // => true
const p2 = isProject(true) // => false
Allows you to write custom inverted TypeGuard
functions.
import { TypeGuardInverted } from 'typesafe-utils'
type Project {
id: number
// ...
}
const isNotProject = <T>(value: T): value is TypeGuardInverted<Project, T> => !value?.hasOwnProperty('id')
const p1 = isNotProject({ id: 1 }) // => false
const p2 = isNotProject(null) // => true