├── .eslintignore ├── .prettierignore ├── .gitignore ├── .vscode └── settings.json ├── src ├── uniqueArray │ ├── uniqueArray.ts │ └── uniqueArray.test.ts ├── pick │ ├── pick.ts │ └── pick.test.ts ├── isNumber │ ├── isNumber.ts │ └── isNumber.test.ts ├── isString │ ├── isString.ts │ └── isString.test.ts ├── isBoolean │ ├── isBoolean.ts │ └── isBoolean.test.ts ├── isObject │ ├── isObject.ts │ └── isObject.test.ts ├── filterDuplicates │ ├── filterDuplicates.ts │ └── filterDuplicates.test.ts ├── filter │ ├── filter.ts │ └── filter.test.ts ├── isArray │ ├── isArray.ts │ └── isArray.test.ts ├── deepClone │ ├── deepClone.ts │ └── deepClone.test.ts ├── is │ ├── is.ts │ └── is.test.ts ├── logical │ ├── logical.ts │ └── logical.test.ts ├── isNull │ ├── isNull.ts │ └── isNull.test.ts ├── isTrue │ ├── isTrue.ts │ └── isTrue.test.ts ├── isTruthy │ ├── isTruthy.ts │ └── isTruthy.test.ts ├── isFalse │ ├── isFalse.ts │ └── isFalse.test.ts ├── isUndefined │ ├── isUndefined.ts │ └── isUndefined.test.ts ├── isZero │ ├── isZero.ts │ └── isZero.test.ts ├── isEmpty │ ├── isEmpty.ts │ └── isEmpty.test.ts ├── types.ts ├── sorting │ ├── sorting.ts │ └── sorting.test.ts └── index.ts ├── tsconfig-cjs.json ├── .prettierrc ├── .nycrc.json ├── .github └── workflows │ ├── pull_request.yml │ └── release.yml ├── .eslintrc ├── tsconfig.json ├── package.json └── README.md /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | lib -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | lib -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .nyc_output 3 | lib 4 | coverage-report.txt -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.codeActionsOnSave": { 3 | "source.fixAll.eslint": true 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /src/uniqueArray/uniqueArray.ts: -------------------------------------------------------------------------------- 1 | 2 | export const uniqueArray = (array: T[]): T[] => Array.from(new Set(array)) 3 | -------------------------------------------------------------------------------- /src/pick/pick.ts: -------------------------------------------------------------------------------- 1 | export const pick = (property: K) => (value: T): T[K] => 2 | value[property] -------------------------------------------------------------------------------- /tsconfig-cjs.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "module": "CommonJS", 5 | "outDir": "./lib/cjs" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/isNumber/isNumber.ts: -------------------------------------------------------------------------------- 1 | import { TypeGuard } from '../types' 2 | 3 | export const isNumber = (value: T): value is TypeGuard => typeof value === 'number' 4 | -------------------------------------------------------------------------------- /src/isString/isString.ts: -------------------------------------------------------------------------------- 1 | import { TypeGuard } from '../types' 2 | 3 | export const isString = (value: T): value is TypeGuard => typeof value === 'string' 4 | -------------------------------------------------------------------------------- /src/isBoolean/isBoolean.ts: -------------------------------------------------------------------------------- 1 | import { TypeGuard } from '../types' 2 | 3 | export const isBoolean = (value: T): value is TypeGuard => typeof value === 'boolean' 4 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "singleQuote": true, 4 | "tabWidth": 3, 5 | "useTabs": true, 6 | "printWidth": 120, 7 | "arrowParens": "always", 8 | "trailingComma": "all" 9 | } 10 | -------------------------------------------------------------------------------- /.nycrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@istanbuljs/nyc-config-typescript", 3 | "all": true, 4 | "check-coverage": true, 5 | "include": ["src/**/*.ts"], 6 | "exclude": ["src/index.ts", "src/**/*.test.ts"] 7 | } 8 | -------------------------------------------------------------------------------- /src/isObject/isObject.ts: -------------------------------------------------------------------------------- 1 | import { TypeGuard } from '../types' 2 | 3 | export const isObject = (value: T): value is TypeGuard => value && typeof value === 'object' 4 | 5 | export const isPrimitiveObject = (value: T): value is TypeGuard => 6 | isObject(value) && ((value)).constructor === Object 7 | -------------------------------------------------------------------------------- /src/filterDuplicates/filterDuplicates.ts: -------------------------------------------------------------------------------- 1 | export const filterDuplicates = (value: T, i: number, a: T[]): boolean => a.findIndex((t) => t === value) === i 2 | 3 | export const filterDuplicatesByKey = (key: K) => ( 4 | value: T, 5 | i: number, 6 | a: T[], 7 | ): boolean => a.findIndex((t) => t[key] === value[key]) === i 8 | -------------------------------------------------------------------------------- /src/filter/filter.ts: -------------------------------------------------------------------------------- 1 | import { InferType } from '../types' 2 | 3 | type Filter = InputType extends ReturnType 4 | ? InputType 5 | : InputType extends unknown 6 | ? ReturnType 7 | : never 8 | 9 | export const createFilter = ( 10 | filterFn: (item: InferType) => boolean, 11 | ): InferType<(item: InferType) => item is Filter, InferType>> => 12 | filterFn as unknown as any 13 | -------------------------------------------------------------------------------- /src/isArray/isArray.ts: -------------------------------------------------------------------------------- 1 | 2 | export type TypeGuard = Type extends Guard ? Type extends ReturnType ? Type : ReturnType : never 3 | 4 | export const isArray = (value: T): value is TypeGuard, T> => Array.isArray(value) 5 | 6 | type NotEmptyArray = [T, ...T[]] 7 | 8 | export const isArrayNotEmpty = (array: T[]): array is NotEmptyArray => !!array?.length 9 | 10 | export const isArrayEmpty = (array: T[]): array is [] => !array?.length 11 | -------------------------------------------------------------------------------- /.github/workflows/pull_request.yml: -------------------------------------------------------------------------------- 1 | name: run lint and tests 2 | 3 | on: 4 | pull_request: 5 | types: [opened, synchronize] 6 | 7 | jobs: 8 | tests: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v2 12 | 13 | - uses: actions/setup-node@v1 14 | with: 15 | node-version: 10 16 | 17 | - run: npm install 18 | 19 | - run: npm run lint 20 | 21 | - run: npm test 22 | 23 | - run: npm run build 24 | -------------------------------------------------------------------------------- /src/deepClone/deepClone.ts: -------------------------------------------------------------------------------- 1 | type ObjectEntriesType = [keyof T, T[keyof T]][] 2 | 3 | export const deepClone = (toClone: T): T => { 4 | if (!toClone || typeof toClone !== 'object') { 5 | return toClone 6 | } 7 | 8 | if (Array.isArray(toClone)) { 9 | return ([...((toClone as unknown) as T[])].map(deepClone) as unknown) as T 10 | } 11 | 12 | return (>Object.entries(toClone)).reduce( 13 | (obj, [key, value]) => ({ ...obj, [key]: deepClone(value) }), 14 | {} as T, 15 | ) 16 | } 17 | -------------------------------------------------------------------------------- /src/is/is.ts: -------------------------------------------------------------------------------- 1 | import { TypeGuard } from '../types' 2 | 3 | export const is = (item: T) => (value: U): value is TypeGuard => value === item 4 | 5 | export const isNot = (item: T) => (value: U): value is U => value !== item 6 | 7 | export const isProperty = (property: K, item: T[K]) => (value: T): boolean => 8 | value[property] === item 9 | 10 | export const isPropertyNot = (property: K, item: T[K]) => (value: T): boolean => 11 | value[property] !== item 12 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parser": "@typescript-eslint/parser", 4 | "plugins": ["@typescript-eslint", "prettier"], 5 | "extends": [ 6 | "eslint:recommended", 7 | "plugin:@typescript-eslint/eslint-recommended", 8 | "plugin:@typescript-eslint/recommended", 9 | "prettier" 10 | ], 11 | "rules": { 12 | "no-console": "error", 13 | "prettier/prettier": "off", 14 | "@typescript-eslint/no-unused-vars": ["off", { "argsIgnorePattern": "^_" }], 15 | "@typescript-eslint/ban-types": "off", 16 | "@typescript-eslint/no-explicit-any": "off" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/isNumber/isNumber.test.ts: -------------------------------------------------------------------------------- 1 | import { suite } from 'uvu' 2 | import * as assert from 'uvu/assert' 3 | import { isNumber } from './isNumber' 4 | 5 | const test = suite('isNumber') 6 | 7 | // isNumber ----------------------------------------------------------------------------------------------------------- 8 | 9 | const items = ['test', { prop: 0 }, null, undefined, true, {}, 0, 123, false, ''] 10 | 11 | test(`isNumber`, () => { 12 | const filteredItems = items.filter(isNumber) 13 | 14 | assert.ok(filteredItems.length === 2) 15 | assert.is(filteredItems[0], 0) 16 | assert.is(filteredItems[1], 123) 17 | }) 18 | 19 | const onlyNumbers = items.filter(isNumber) 20 | onlyNumbers[0] && onlyNumbers[0].toFixed() 21 | 22 | test.run() 23 | -------------------------------------------------------------------------------- /src/isString/isString.test.ts: -------------------------------------------------------------------------------- 1 | import { suite } from 'uvu' 2 | import * as assert from 'uvu/assert' 3 | import { isString } from './isString' 4 | 5 | const test = suite('isString') 6 | 7 | // isString ----------------------------------------------------------------------------------------------------------- 8 | 9 | const items = ['test', { prop: 0 }, null, undefined, true, {}, 0, 123, false, ''] 10 | 11 | test(`isString`, () => { 12 | const filteredItems = items.filter(isString) 13 | 14 | assert.ok(filteredItems.length === 2) 15 | assert.is(filteredItems[0], 'test') 16 | assert.is(filteredItems[1], '') 17 | }) 18 | 19 | const onlyStrings = items.filter(isString) 20 | onlyStrings[0] && onlyStrings[0].toLowerCase() 21 | 22 | test.run() 23 | -------------------------------------------------------------------------------- /src/isBoolean/isBoolean.test.ts: -------------------------------------------------------------------------------- 1 | import { suite } from 'uvu' 2 | import * as assert from 'uvu/assert' 3 | import { isBoolean } from './isBoolean' 4 | 5 | const test = suite('isBoolean') 6 | 7 | // isBoolean ---------------------------------------------------------------------------------------------------------- 8 | 9 | const items = ['test', { prop: 0 }, null, undefined, true, {}, 0, 123, false, ''] 10 | 11 | test(`isBoolean`, () => { 12 | const filteredItems = items.filter(isBoolean) 13 | 14 | assert.ok(filteredItems.length === 2) 15 | assert.is(filteredItems[0], true) 16 | assert.is(filteredItems[1], false) 17 | }) 18 | 19 | const onlyBooleans = items.filter(isBoolean) 20 | onlyBooleans[0] && onlyBooleans[0] === true 21 | 22 | test.run() 23 | -------------------------------------------------------------------------------- /src/logical/logical.ts: -------------------------------------------------------------------------------- 1 | import type { FilterFn } from '../types' 2 | 3 | export const and = 4 | (...filters: FilterFn[]) => 5 | (value: T, index?: number, array?: T[]): value is U => 6 | filters.reduce((prev, filter) => prev && (filter as any)(value, index, array), true) 7 | 8 | export const or = 9 | (...filters: FilterFn[]) => 10 | (value: T, index?: number, array?: T[]): value is U => 11 | filters.reduce((prev, filter) => prev || (filter as any)(value, index, array), false) 12 | 13 | export const not = 14 | = FilterFn>(filterFn: F) => 15 | (value: T, i?: number, array?: T[]): value is U => 16 | !filterFn(value, i, array) 17 | -------------------------------------------------------------------------------- /src/isNull/isNull.ts: -------------------------------------------------------------------------------- 1 | import { TypeGuard, TypeGuardInverted } from '../types' 2 | 3 | export const isNull = (value: T): value is TypeGuard => value === null 4 | 5 | export const isNotNull = (value: T): value is TypeGuardInverted => value !== null 6 | 7 | export const isPropertyNull = (property: K) => (value: T): boolean => 8 | value[property] === null 9 | 10 | export const isPropertyNotNull = (property: K) => (value: T): boolean => 11 | value[property] !== null 12 | 13 | export const arePropertiesNull = (...properties: K[]) => (value: T): boolean => 14 | properties.every((property) => value[property] === null) 15 | 16 | export const arePropertiesNotNull = (...properties: K[]) => (value: T): boolean => 17 | properties.every((property) => value[property] !== null) 18 | -------------------------------------------------------------------------------- /src/isTrue/isTrue.ts: -------------------------------------------------------------------------------- 1 | import { TypeGuard, TypeGuardInverted } from '../types' 2 | 3 | export const isTrue = (value: T): value is TypeGuard => value === true 4 | 5 | export const isNotTrue = (value: T): value is TypeGuardInverted => value !== true 6 | 7 | export const isPropertyTrue = (property: K) => (value: T): boolean => 8 | value[property] === true 9 | 10 | export const isPropertyNotTrue = (property: K) => (value: T): boolean => 11 | value[property] !== true 12 | 13 | export const arePropertiesTrue = (...properties: K[]) => (value: T): boolean => 14 | properties.every((property) => value[property] === true) 15 | 16 | export const arePropertiesNotTrue = (...properties: K[]) => (value: T): boolean => 17 | properties.every((property) => value[property] !== true) 18 | -------------------------------------------------------------------------------- /src/isTruthy/isTruthy.ts: -------------------------------------------------------------------------------- 1 | import { FalsyType, TypeGuard, TypeGuardInverted } from '../types' 2 | 3 | export type Truthy = TypeGuardInverted 4 | 5 | export type Falsy = TypeGuard 6 | 7 | export const isTruthy = (value: T): value is Truthy => !!value 8 | 9 | export const isFalsy = (value: T): value is Falsy => !value 10 | 11 | export const isPropertyTruthy = (property: K) => (value: T): boolean => 12 | !!value[property] 13 | 14 | export const isPropertyFalsy = (property: K) => (value: T): boolean => !value[property] 15 | 16 | export const arePropertiesTruthy = (...properties: K[]) => (value: T): boolean => 17 | properties.every((property) => !!value[property]) 18 | 19 | export const arePropertiesFalsy = (...properties: K[]) => (value: T): boolean => 20 | properties.every((property) => !value[property]) 21 | -------------------------------------------------------------------------------- /src/isFalse/isFalse.ts: -------------------------------------------------------------------------------- 1 | import { TypeGuard, TypeGuardInverted } from '../types' 2 | 3 | export const isFalse = (value: T): value is TypeGuard => value === false 4 | 5 | export const isNotFalse = (value: T): value is TypeGuardInverted => value !== false 6 | 7 | export const isPropertyFalse = (property: K) => (value: T): boolean => 8 | value[property] === false 9 | 10 | export const isPropertyNotFalse = (property: K) => (value: T): boolean => 11 | value[property] !== false 12 | 13 | export const arePropertiesFalse = (...properties: K[]) => (value: T): boolean => 14 | properties.every((property) => value[property] === false) 15 | 16 | export const arePropertiesNotFalse = (...properties: K[]) => (value: T): boolean => 17 | properties.every((property) => value[property] !== false) 18 | -------------------------------------------------------------------------------- /src/isUndefined/isUndefined.ts: -------------------------------------------------------------------------------- 1 | import { TypeGuard, TypeGuardInverted } from '../types' 2 | 3 | export const isUndefined = (value: T): value is TypeGuard => value === undefined 4 | 5 | export const isNotUndefined = (value: T): value is TypeGuardInverted => value !== undefined 6 | 7 | export const isPropertyUndefined = (property: K) => (value: T): boolean => 8 | value[property] === undefined 9 | 10 | export const isPropertyNotUndefined = (property: K) => (value: T): boolean => 11 | value[property] !== undefined 12 | 13 | export const arePropertiesUndefined = (...properties: K[]) => (value: T): boolean => 14 | properties.every((property) => value[property] === undefined) 15 | 16 | export const arePropertiesNotUndefined = (...properties: K[]) => (value: T): boolean => 17 | properties.every((property) => value[property] !== undefined) 18 | -------------------------------------------------------------------------------- /src/isZero/isZero.ts: -------------------------------------------------------------------------------- 1 | import { TypeGuard, TypeGuardWithReturnType, TypeGuardInverted } from '../types' 2 | 3 | export const isZero = (value: T | 0): value is TypeGuardWithReturnType => value === 0 4 | 5 | export const isNotZero = (value: T): value is TypeGuardInverted<0, T> => value !== 0 6 | 7 | export const isPropertyZero = (property: K) => (value: T): boolean => 8 | value[property] === 0 9 | 10 | export const isPropertyNotZero = (property: K) => (value: T): boolean => 11 | value[property] !== 0 12 | 13 | export const arePropertiesZero = (...properties: K[]) => (value: T): boolean => 14 | properties.every((property) => value[property] === 0) 15 | 16 | export const arePropertiesNotZero = (...properties: K[]) => (value: T): boolean => 17 | properties.every((property) => value[property] !== 0) 18 | -------------------------------------------------------------------------------- /src/isEmpty/isEmpty.ts: -------------------------------------------------------------------------------- 1 | import { TypeGuard, TypeGuardWithReturnType, TypeGuardInverted } from '../types' 2 | 3 | export const isEmpty = (value: T | ''): value is TypeGuardWithReturnType => value === '' 4 | 5 | export const isNotEmpty = (value: T): value is TypeGuardInverted => value !== '' 6 | 7 | export const isPropertyEmpty = (property: K) => (value: T): boolean => 8 | value[property] === '' 9 | 10 | export const isPropertyNotEmpty = (property: K) => (value: T): boolean => 11 | value[property] !== '' 12 | 13 | export const arePropertiesEmpty = (...properties: K[]) => (value: T): boolean => 14 | properties.every((property) => value[property] === '') 15 | 16 | export const arePropertiesNotEmpty = (...properties: K[]) => (value: T): boolean => 17 | properties.every((property) => value[property] !== '') 18 | -------------------------------------------------------------------------------- /src/filter/filter.test.ts: -------------------------------------------------------------------------------- 1 | import { suite } from 'uvu' 2 | import * as assert from 'uvu/assert' 3 | import { createFilter } from './filter' 4 | 5 | const test = suite('filter') 6 | 7 | // filter --------------------------------------------------------------------------------------------------- 8 | 9 | test(`filter for 1`, () => { 10 | const items = [1, 2, 3, 5, 8, 1] 11 | const filterFor1 = createFilter<1, number>((item) => item === 1) 12 | const filteredItems = items.filter(filterFor1) 13 | 14 | assert.ok(filteredItems.length === 2) 15 | }) 16 | 17 | test(`filter for id not null`, () => { 18 | type T = { id: number | null } 19 | const item1: T = { id: 1 } 20 | const item2: T = { id: null } 21 | const items = [item1, item2, item1] 22 | const filterForIdNotNull = createFilter<{ id: number }, T>((item) => item.id !== null) 23 | const filteredItems = items.filter(filterForIdNotNull) 24 | 25 | assert.ok(filteredItems.length === 2) 26 | assert.ok(filteredItems.includes(item1 as any)) 27 | }) 28 | 29 | test.run() 30 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | export type Everything = {} | null | undefined 2 | 3 | export type FalsyType = false | '' | 0 | null | undefined 4 | 5 | export type TypeGuard = 6 | Type extends Guard 7 | ? Type 8 | : never 9 | 10 | export type TypeGuardWithReturnType = 11 | Type extends Guard 12 | ? Type extends ReturnType 13 | ? Type 14 | : ReturnType 15 | : never 16 | 17 | export type TypeGuardInverted = 18 | Type extends Guard 19 | ? never 20 | : Type 21 | 22 | export type TypeGuardInvertedWithReturnType = 23 | Type extends Guard 24 | ? never 25 | : Type extends ReturnType 26 | ? Type 27 | : ReturnType 28 | 29 | export type FilterFn = 30 | | ((value: InputType, i?: number, array?: InputType[]) => boolean) 31 | | ((value: InputType, i?: number, array?: InputType[]) => value is ReturnType) 32 | 33 | export type InferType = OriginalType extends infer InferredType ? InferredType : OriginalType 34 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2019", 4 | "module": "ES2020", 5 | "moduleResolution": "Node", 6 | "declaration": true, 7 | // "sourceMap": true, 8 | // "declarationMap": true, 9 | "outDir": "./lib/esm", 10 | "rootDir": "./src", 11 | "strict": true, 12 | "noImplicitAny": true, 13 | "strictNullChecks": true, 14 | "strictFunctionTypes": true, 15 | "strictBindCallApply": true, 16 | "strictPropertyInitialization": true, 17 | "noImplicitThis": true, 18 | "alwaysStrict": true, 19 | // "noUnusedLocals": true, 20 | "noUnusedParameters": true, 21 | "noImplicitReturns": true, 22 | "noFallthroughCasesInSwitch": true, 23 | "noUncheckedIndexedAccess": true, 24 | "esModuleInterop": true, 25 | "skipLibCheck": true, 26 | "forceConsistentCasingInFileNames": true 27 | }, 28 | "include": ["src/**/*"], 29 | "exclude": ["node_modules"], 30 | "ts-node": { 31 | "compilerOptions": { 32 | "module": "commonjs", 33 | "noUnusedLocals": false 34 | }, 35 | "include": ["src/**/*.test.*"] 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/uniqueArray/uniqueArray.test.ts: -------------------------------------------------------------------------------- 1 | import { suite } from 'uvu' 2 | import * as assert from 'uvu/assert' 3 | import { uniqueArray } from './uniqueArray' 4 | 5 | const test = suite('uniqueArray') 6 | 7 | // -------------------------------------------------------------------------------------------------------------------- 8 | 9 | const testUniqueArray = (input: T[], expexted: T[]) => 10 | test('uniqueArray', () => assert.equal(uniqueArray(input), expexted)) 11 | 12 | testUniqueArray([], []) 13 | 14 | testUniqueArray(['test'], ['test']) 15 | testUniqueArray(['test', '123'], ['test', '123']) 16 | testUniqueArray(['a', 'a'], ['a']) 17 | testUniqueArray(['a', 'b', 'a', 'a'], ['a', 'b']) 18 | 19 | testUniqueArray([123], [123]) 20 | testUniqueArray([123, 456], [123, 456]) 21 | testUniqueArray([1, 1], [1]) 22 | testUniqueArray([1, 2, 1, 1], [1, 2]) 23 | 24 | testUniqueArray([true, false], [true, false]) 25 | testUniqueArray([true, true], [true]) 26 | testUniqueArray([false, false, false], [false]) 27 | 28 | testUniqueArray(['test', 123, true, null, false], ['test', 123, true, null, false]) 29 | 30 | test.run() 31 | -------------------------------------------------------------------------------- /src/pick/pick.test.ts: -------------------------------------------------------------------------------- 1 | import { suite } from 'uvu' 2 | import * as assert from 'uvu/assert' 3 | import { pick } from './pick' 4 | 5 | const test = suite('pick') 6 | 7 | // pick ----------------------------------------------------------------------------------------------- 8 | 9 | test(`pick inStock`, () => { 10 | const items = [ 11 | { available: 'some-id', inStock: true }, 12 | { available: true, inStock: true }, 13 | { available: 'some-id', inStock: false }, 14 | { available: null, inStock: true }, 15 | ] 16 | const filteredItems = items.map(pick('inStock')) 17 | 18 | filteredItems.forEach(item => assert.is(typeof item, 'boolean')) 19 | assert.is(filteredItems.filter(item => item === true).length, 3) 20 | assert.is(filteredItems.filter(item => item === false).length, 1) 21 | }) 22 | 23 | test(`pick length`, () => { 24 | const items = ["string", "another-string", ""] 25 | const filteredItems = items.map(pick('length')) 26 | 27 | filteredItems.forEach(item => assert.is(typeof item, 'number')) 28 | assert.is(filteredItems[0], 6) 29 | assert.is(filteredItems[1], 14) 30 | assert.is(filteredItems[2], 0) 31 | }) 32 | 33 | test.run() 34 | -------------------------------------------------------------------------------- /src/isObject/isObject.test.ts: -------------------------------------------------------------------------------- 1 | import { suite } from 'uvu' 2 | import * as assert from 'uvu/assert' 3 | import { isObject, isPrimitiveObject } from './isObject' 4 | 5 | const test = suite('isObject') 6 | 7 | // isObject ----------------------------------------------------------------------------------------------------------- 8 | 9 | const now = new Date() 10 | const items = ['test', { prop: 0 } as SomeObject, null, undefined, true, {}, 0, 123, false, ''] 11 | 12 | type SomeObject = { 13 | prop?: number 14 | } 15 | 16 | test(`isObject`, () => { 17 | const filteredItems = [...items, now].filter(isObject) 18 | 19 | assert.ok(filteredItems.length === 3) 20 | assert.equal(filteredItems[0], { prop: 0 }) 21 | assert.equal(filteredItems[1], {}) 22 | assert.equal(filteredItems[2], now) 23 | }) 24 | 25 | test(`isPrimitiveObject`, () => { 26 | const filteredItems = items.filter(isPrimitiveObject) 27 | 28 | assert.ok(filteredItems.length === 2) 29 | assert.equal(filteredItems[0], { prop: 0 }) 30 | assert.equal(filteredItems[1], {}) 31 | }) 32 | 33 | const onlyObjects = items.filter(isPrimitiveObject) 34 | onlyObjects[0] && onlyObjects[0].prop 35 | 36 | test.run() 37 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Update version & publish to npm 2 | 3 | on: 4 | push: 5 | branches: 6 | - 'main' 7 | 8 | jobs: 9 | publish: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v2 13 | 14 | - name: install node 15 | uses: actions/setup-node@v1 16 | with: 17 | node-version: 10 18 | 19 | - run: npm install 20 | 21 | - name: run test 22 | run: npm test 23 | 24 | - run: npm run coverage 25 | 26 | - name: output test coverage 27 | run: npm run coverage:output-report 28 | 29 | - uses: actions/upload-artifact@v2 30 | with: 31 | name: coverage-report 32 | path: coverage-report.txt 33 | 34 | - name: build package 35 | run: npm run build 36 | 37 | - name: bump version and create tag 38 | uses: 'phips28/gh-action-bump-version@master' 39 | env: 40 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 41 | with: 42 | minor-wording: 'feat,feature' 43 | 44 | - name: publish to npm 45 | uses: JS-DevTools/npm-publish@v1 46 | with: 47 | token: ${{ secrets.NPM_TOKEN }} 48 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "typesafe-utils", 3 | "version": "1.16.2", 4 | "description": "A few small typesafe utilities", 5 | "homepage": "https://github.com/ivanhofer/typesafe-utils", 6 | "repository": "https://github.com/ivanhofer/typesafe-utils", 7 | "author": "ivanhofer", 8 | "keywords": [ 9 | "typescript", 10 | "utilities", 11 | "lightweight", 12 | "small", 13 | "typesafe", 14 | "utils" 15 | ], 16 | "license": "MIT", 17 | "main": "lib/cjs/index.js", 18 | "module": "lib/esm/index.js", 19 | "files": [ 20 | "/lib" 21 | ], 22 | "types": "lib/esm/index.d.ts", 23 | "scripts": { 24 | "build": "tsc && tsc -p tsconfig-cjs.json", 25 | "lint": "eslint src", 26 | "lint:fix": "eslint src --fix", 27 | "test": "uvu -r ts-node/register src .*\\.test\\..*", 28 | "test:watch": "watchlist src -- npm test", 29 | "coverage": "nyc npm run test", 30 | "coverage:output-report": "nyc report > coverage-report.txt" 31 | }, 32 | "devDependencies": { 33 | "@istanbuljs/nyc-config-typescript": "^1.0.1", 34 | "@types/node": "^16.7.1", 35 | "@typescript-eslint/eslint-plugin": "^4.29.3", 36 | "@typescript-eslint/parser": "^4.29.3", 37 | "eslint": "^7.32.0", 38 | "eslint-config-prettier": "^8.3.0", 39 | "eslint-plugin-prettier": "^3.4.1", 40 | "nyc": "^15.1.0", 41 | "prettier": "^2.3.2", 42 | "ts-node": "^10.2.1", 43 | "typescript": "^4.3.5", 44 | "uvu": "^0.5.1", 45 | "watchlist": "^0.2.3" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/is/is.test.ts: -------------------------------------------------------------------------------- 1 | import { suite } from 'uvu' 2 | import * as assert from 'uvu/assert' 3 | import { is, isNot, isProperty, isPropertyNot } from './is' 4 | 5 | const test = suite('is') 6 | 7 | // is ----------------------------------------------------------------------------------------------------------------- 8 | 9 | test(`is 5`, () => { 10 | const items = [1, 5, 8, 5, 33] 11 | const filteredItems = items.filter(is(5)) 12 | 13 | assert.ok(filteredItems.length === 2) 14 | }) 15 | 16 | // isNot -------------------------------------------------------------------------------------------------------------- 17 | 18 | test(`isNot 'text'`, () => { 19 | const items = ['text', 'another-text', null, ''] 20 | const filteredItems = items.filter(isNot('text')) 21 | 22 | assert.ok(filteredItems.length === 3) 23 | }) 24 | 25 | // isProperty --------------------------------------------------------------------------------------------------------- 26 | 27 | test(`isProperty id 42`, () => { 28 | const items = [{ id: 46 }, { id: 42 }, { id: undefined }] 29 | const filteredItems = items.filter(isProperty('id', 42)) 30 | 31 | assert.ok(filteredItems.length === 1) 32 | }) 33 | 34 | // isPropertyNot ------------------------------------------------------------------------------------------------------ 35 | 36 | test(`isPropertyNot id null`, () => { 37 | const items = [{ id: 1 }, { id: null }, { id: undefined }] 38 | const filteredItems = items.filter(isPropertyNot('id', null)) 39 | 40 | assert.ok(filteredItems.length === 2) 41 | }) 42 | 43 | test.run() 44 | -------------------------------------------------------------------------------- /src/isArray/isArray.test.ts: -------------------------------------------------------------------------------- 1 | import { suite } from 'uvu' 2 | import * as assert from 'uvu/assert' 3 | import { isArray, isArrayEmpty, isArrayNotEmpty } from './isArray' 4 | 5 | const test = suite('isArray') 6 | 7 | // isArray ------------------------------------------------------------------------------------------------------------ 8 | 9 | const items = ['test', { prop: 0 }, null, undefined, true, {}, [123, 77], 0, 123, false, '', []] 10 | 11 | test(`isArray`, () => { 12 | const filteredItems = items.filter(isArray) 13 | 14 | assert.ok(filteredItems.length === 2) 15 | assert.ok(filteredItems[0] && filteredItems[0].length === 2) 16 | assert.ok(filteredItems[1] && filteredItems[1].length === 0) 17 | }) 18 | 19 | const onlyArrays = items.filter(isArray) 20 | onlyArrays[0] && onlyArrays[0][0] && onlyArrays[0][0].toFixed() 21 | 22 | // isArrayEmpty ------------------------------------------------------------------------------------------------------- 23 | 24 | test(`isArrayEmpty`, () => { 25 | assert.equal(isArrayEmpty([]), true) 26 | assert.equal(isArrayEmpty([123, 123]), false) 27 | }) 28 | 29 | const emptyArray: string[] = [] 30 | if (isArrayEmpty(emptyArray)) { 31 | emptyArray.length === 0 32 | } 33 | 34 | // isArrayNotEmpty ---------------------------------------------------------------------------------------------------- 35 | 36 | test(`isArrayNotEmpty`, () => { 37 | assert.equal(isArrayNotEmpty([]), false) 38 | assert.equal(isArrayNotEmpty([123, 123]), true) 39 | }) 40 | 41 | const nonEmptyArray = ['hi'] 42 | if (isArrayNotEmpty(nonEmptyArray)) { 43 | nonEmptyArray[0].toUpperCase() 44 | } 45 | 46 | test.run() 47 | -------------------------------------------------------------------------------- /src/deepClone/deepClone.test.ts: -------------------------------------------------------------------------------- 1 | import { suite } from 'uvu' 2 | import { deepClone } from './deepClone' 3 | import * as assert from 'uvu/assert' 4 | 5 | const test = suite('deepClone') 6 | 7 | const check = (toClone: T) => assert.equal(deepClone(toClone), toClone) 8 | 9 | // primitives --------------------------------------------------------------------------------------------------------- 10 | 11 | test('null', () => check(null)) 12 | 13 | test('undefined', () => check(undefined)) 14 | 15 | test('boolean true', () => check(true)) 16 | test('boolean false', () => check(false)) 17 | 18 | test('number 0', () => check(0)) 19 | test('number 1', () => check(1)) 20 | test('number -123', () => check(-123)) 21 | 22 | test('infinity', () => check(Infinity)) 23 | test('negativ infinity', () => check(-Infinity)) 24 | 25 | test('string', () => check('test')) 26 | test('string with spaces', () => check('this is a with some spaces')) 27 | 28 | // arrays ------------------------------------------------------------------------------------------------------------- 29 | 30 | test('empty array', () => check([])) 31 | test('array with single value', () => check([5])) 32 | test('array with multiple value', () => check([1, 5, 8])) 33 | test('array with mixed values', () => check([null, 'test', 3])) 34 | 35 | test('nested empty arrays', () => check([[[[]], []]])) 36 | test('nested arrays with values', () => check([[true], [123, [5]]])) 37 | 38 | // objects ------------------------------------------------------------------------------------------------------------ 39 | 40 | test('empty object', () => check({})) 41 | test('object with single attribute', () => check({ prop: 'test' })) 42 | test('object with multiple attributes', () => check({ a: 2, b: Infinity })) 43 | 44 | test('nested empty objects', () => check({ a: { b: { c: { d: {} } } } })) 45 | 46 | test.run() 47 | -------------------------------------------------------------------------------- /src/filterDuplicates/filterDuplicates.test.ts: -------------------------------------------------------------------------------- 1 | import { suite } from 'uvu' 2 | import * as assert from 'uvu/assert' 3 | import { filterDuplicates, filterDuplicatesByKey } from './filterDuplicates' 4 | 5 | const test = suite('filterDuplicates') 6 | 7 | // filterDuplicates --------------------------------------------------------------------------------------------------- 8 | 9 | test(`filter duplicate numbers`, () => { 10 | const items = [1, 2, 3, 5, 8, 1] 11 | const filteredItems = items.filter(filterDuplicates) 12 | 13 | assert.ok(filteredItems.length === 5) 14 | assert.ok(filteredItems.filter((nr) => nr === 1).length === 1) 15 | }) 16 | 17 | test(`filter duplicate objects`, () => { 18 | const item1 = { id: 1 } 19 | const item2 = { id: 2 } 20 | const items = [item1, item2, item1] 21 | const filteredItems = items.filter(filterDuplicates) 22 | 23 | assert.ok(filteredItems.length === 2) 24 | assert.ok(filteredItems.includes(item2)) 25 | assert.ok(filteredItems.filter((item) => item === item1).length === 1) 26 | }) 27 | 28 | // filterDuplicatesByKey ---------------------------------------------------------------------------------------------- 29 | 30 | test(`filter duplicate IDs`, () => { 31 | const items = [{ id: 1 }, { id: 2 }, { id: 1 }] 32 | const filteredItems = items.filter(filterDuplicatesByKey('id')) 33 | 34 | assert.ok(filteredItems.length === 2) 35 | assert.ok(filteredItems.filter(({ id }) => id === 1).length === 1) 36 | }) 37 | 38 | test(`filter duplicate names`, () => { 39 | const items = [ 40 | { id: 1, name: 'name-1' }, 41 | { id: 2, name: 'name-2' }, 42 | { id: 3, name: 'name-1' }, 43 | { id: 4, name: 'name-2' }, 44 | ] 45 | const filteredItems = items.filter(filterDuplicatesByKey('name')) 46 | 47 | assert.ok(filteredItems.length === 2) 48 | assert.ok(filteredItems.filter(({ name }) => name === 'name-1').length === 1) 49 | const item2 = filteredItems.find(({ name }) => name === 'name-2') 50 | assert.ok(item2 && item2.id === 2) 51 | }) 52 | 53 | test.run() 54 | -------------------------------------------------------------------------------- /src/logical/logical.test.ts: -------------------------------------------------------------------------------- 1 | import { suite } from 'uvu' 2 | import * as assert from 'uvu/assert' 3 | import { isNotUndefined } from '..' 4 | import { isNotNull, isNull } from '../isNull/isNull' 5 | import { isNumber } from '../isNumber/isNumber' 6 | import { isString } from '../isString/isString' 7 | import { isFalsy, isTruthy } from '../isTruthy/isTruthy' 8 | import { and, not, or } from './logical' 9 | 10 | const test = suite('logical') 11 | 12 | const items = ['test', { prop: 0 }, null, undefined, true, {}, 0, 123, false, ''] 13 | 14 | // and ---------------------------------------------------------------------------------------------------------------- 15 | 16 | test(`and truthy`, () => { 17 | const filteredItems = items.filter(and(isTruthy, isFalsy)) 18 | 19 | assert.ok(filteredItems.length === 0) 20 | }) 21 | 22 | test(`and isNumber && equals 4`, () => { 23 | const filteredItems = [1.5, 4, -4, 6, 4].filter(and(isNumber, (value) => value === 4)) 24 | 25 | assert.ok(filteredItems.length === 2) 26 | }) 27 | 28 | // or ----------------------------------------------------------------------------------------------------------------- 29 | 30 | test(`or truthy`, () => { 31 | const filteredItems = items.filter(or(isTruthy, isFalsy)) 32 | 33 | assert.ok(filteredItems.length === items.length) 34 | }) 35 | 36 | test(`and isString || isNull`, () => { 37 | const filteredItems = ['', null, 'test', 55, undefined].filter(or(isString, isNull)) 38 | 39 | assert.ok(filteredItems.length === 3) 40 | }) 41 | 42 | // not ---------------------------------------------------------------------------------------------------------------- 43 | 44 | test(`not isNull`, () => { 45 | const items = [1, 5, 8, 5, 33, null] 46 | const filteredItems = items.filter(not(isNull)) 47 | 48 | assert.ok(filteredItems.length === 5) 49 | }) 50 | 51 | test(`not isNotNull`, () => { 52 | const items = [1, 5, 8, 5, 33, null] 53 | const filteredItems = items.filter(not(isNotNull)) 54 | 55 | assert.ok(filteredItems.length === 1) 56 | }) 57 | 58 | const _explicitType: number[] = [null, 123].filter(not(isNotNull)) 59 | const _autoType: number[] = [123, undefined].filter(not(isNotUndefined)) 60 | 61 | test.run() 62 | -------------------------------------------------------------------------------- /src/sorting/sorting.ts: -------------------------------------------------------------------------------- 1 | const sortASC = (a: T, b: T) => { 2 | if (a === b) return 0 3 | if (a == null) return -1 4 | if (b == null) return 1 5 | return a < b ? -1 : 1 6 | } 7 | 8 | // number ------------------------------------------------------------------------------------------------------------- 9 | 10 | type NumberType = number | undefined | null 11 | 12 | export const sortNumberASC = (a?: NumberType, b?: NumberType): number => sortASC(a, b) 13 | 14 | export const sortNumberPropertyASC = ( 15 | property: K, 16 | ) => ({ [property]: a }: T, { [property]: b }: T): number => sortNumberASC(a, b) 17 | 18 | export const sortNumberDESC = (a?: NumberType, b?: NumberType): number => sortNumberASC(b, a) 19 | 20 | export const sortNumberPropertyDESC = ( 21 | property: K, 22 | ) => ({ [property]: a }: T, { [property]: b }: T): number => sortNumberDESC(a, b) 23 | 24 | // string ------------------------------------------------------------------------------------------------------------- 25 | 26 | type StringType = string | undefined | null 27 | 28 | export const sortStringASC = (a?: StringType, b?: StringType): number => sortASC(a?.toLowerCase(), b?.toLowerCase()) 29 | 30 | export const sortStringPropertyASC = ( 31 | property: K, 32 | ) => ({ [property]: a }: T, { [property]: b }: T): number => sortStringASC(a, b) 33 | 34 | export const sortStringDESC = (a?: StringType, b?: StringType): number => sortStringASC(b, a) 35 | 36 | export const sortStringPropertyDESC = ( 37 | property: K, 38 | ) => ({ [property]: a }: T, { [property]: b }: T): number => sortStringDESC(a, b) 39 | 40 | // date --------------------------------------------------------------------------------------------------------------- 41 | 42 | type DateType = Date | undefined | null 43 | 44 | export const sortDateASC = (a?: DateType, b?: DateType): number => sortASC(a?.getTime(), b?.getTime()) 45 | 46 | export const sortDatePropertyASC = (property: K) => ( 47 | { [property]: a }: T, 48 | { [property]: b }: T, 49 | ): number => sortASC(a?.getTime(), b?.getTime()) 50 | 51 | export const sortDateDESC = (a?: DateType, b?: DateType): number => sortDateASC(b, a) 52 | 53 | export const sortDatePropertyDESC = (property: K) => ( 54 | { [property]: a }: T, 55 | { [property]: b }: T, 56 | ): number => sortDateDESC(a, b) 57 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export type { TypeGuard, TypeGuardWithReturnType, TypeGuardInverted, TypeGuardInvertedWithReturnType } from './types' 2 | export type { Truthy, Falsy } from './isTruthy/isTruthy' 3 | 4 | export { deepClone } from './deepClone/deepClone' 5 | 6 | export { createFilter } from './filter/filter' 7 | 8 | export { filterDuplicates, filterDuplicatesByKey } from './filterDuplicates/filterDuplicates' 9 | 10 | export { is, isNot, isProperty, isPropertyNot } from './is/is' 11 | 12 | export { isArray, isArrayEmpty, isArrayNotEmpty } from './isArray/isArray' 13 | 14 | export { 15 | isEmpty, 16 | isNotEmpty, 17 | isPropertyEmpty, 18 | isPropertyNotEmpty, 19 | arePropertiesEmpty, 20 | arePropertiesNotEmpty, 21 | } from './isEmpty/isEmpty' 22 | 23 | export { 24 | isFalse, 25 | isNotFalse, 26 | isPropertyFalse, 27 | isPropertyNotFalse, 28 | arePropertiesFalse, 29 | arePropertiesNotFalse, 30 | } from './isFalse/isFalse' 31 | 32 | export { 33 | isNull, 34 | isNotNull, 35 | isPropertyNull, 36 | isPropertyNotNull, 37 | arePropertiesNull, 38 | arePropertiesNotNull, 39 | } from './isNull/isNull' 40 | 41 | export { 42 | isTrue, 43 | isNotTrue, 44 | isPropertyTrue, 45 | isPropertyNotTrue, 46 | arePropertiesTrue, 47 | arePropertiesNotTrue, 48 | } from './isTrue/isTrue' 49 | 50 | export { 51 | isTruthy, 52 | isFalsy, 53 | isPropertyTruthy, 54 | isPropertyFalsy, 55 | arePropertiesTruthy, 56 | arePropertiesFalsy, 57 | } from './isTruthy/isTruthy' 58 | 59 | export { 60 | isUndefined, 61 | isNotUndefined, 62 | isPropertyUndefined, 63 | isPropertyNotUndefined, 64 | arePropertiesUndefined, 65 | arePropertiesNotUndefined, 66 | } from './isUndefined/isUndefined' 67 | 68 | export { 69 | isZero, 70 | isNotZero, 71 | isPropertyZero, 72 | isPropertyNotZero, 73 | arePropertiesZero, 74 | arePropertiesNotZero, 75 | } from './isZero/isZero' 76 | 77 | export { isObject, isPrimitiveObject } from './isObject/isObject' 78 | 79 | export { isString } from './isString/isString' 80 | 81 | export { isNumber } from './isNumber/isNumber' 82 | 83 | export { isBoolean } from './isBoolean/isBoolean' 84 | 85 | export { and, or, not } from './logical/logical' 86 | 87 | export { pick } from './pick/pick' 88 | 89 | export { 90 | sortNumberASC, 91 | sortNumberDESC, 92 | sortNumberPropertyASC, 93 | sortNumberPropertyDESC, 94 | sortStringASC, 95 | sortStringDESC, 96 | sortStringPropertyASC, 97 | sortStringPropertyDESC, 98 | sortDateASC, 99 | sortDateDESC, 100 | sortDatePropertyASC, 101 | sortDatePropertyDESC, 102 | } from './sorting/sorting' 103 | 104 | export { uniqueArray } from './uniqueArray/uniqueArray' 105 | -------------------------------------------------------------------------------- /src/isNull/isNull.test.ts: -------------------------------------------------------------------------------- 1 | import { suite } from 'uvu' 2 | import * as assert from 'uvu/assert' 3 | 4 | import { isNull, isNotNull, isPropertyNull, isPropertyNotNull, arePropertiesNotNull, arePropertiesNull } from './isNull' 5 | 6 | const test = suite('null') 7 | 8 | const nullValues = [null] 9 | const notNullValues = [-Infinity, -1, 1, Infinity, 'test', true, [], {}, undefined, 0, '', false] 10 | 11 | // isNull ------------------------------------------------------------------------------------------------------------- 12 | 13 | nullValues.forEach((value) => test(`isNull ${value}`, () => assert.ok(isNull(value)))) 14 | notNullValues.forEach((value) => test(`! isNull ${value}`, () => assert.not(isNull(value)))) 15 | 16 | const _nullType: null[] = [...nullValues, notNullValues].filter(isNull) 17 | const _neverType: never[] = notNullValues.filter(isNull) 18 | 19 | // isNotNull ---------------------------------------------------------------------------------------------------------- 20 | 21 | notNullValues.forEach((value) => test(`isNotNull ${value}`, () => assert.ok(isNotNull(value)))) 22 | nullValues.forEach((value) => test(`! isNotNull ${value}`, () => assert.not(isNotNull(value)))) 23 | 24 | const _allNotType: ({} | undefined)[] = [...nullValues, ...notNullValues].filter(isNotNull) 25 | const _neverNotType: never[] = nullValues.filter(isNotNull) 26 | 27 | // isPropertyNull ----------------------------------------------------------------------------------------------------- 28 | 29 | test(`isPropertyNotNull id`, () => { 30 | const items = [{ id: 0 }, { id: undefined }, { id: null }] 31 | const filteredItems = items.filter(isPropertyNull('id')) 32 | 33 | assert.ok(filteredItems.length === 1) 34 | }) 35 | 36 | // isPropertyNotUndefined --------------------------------------------------------------------------------------------- 37 | 38 | test(`isPropertyNotNull id`, () => { 39 | const items = [{ id: undefined }, { id: 1 }, { id: null }] 40 | const filteredItems = items.filter(isPropertyNotNull('id')) 41 | 42 | assert.ok(filteredItems.length === 2) 43 | }) 44 | 45 | // arePropertiesNull -------------------------------------------------------------------------------------------------- 46 | 47 | test(`arePropertiesNull id name`, () => { 48 | const items = [ 49 | { id: null, name: 'name-1' }, 50 | { id: 0, name: null }, 51 | { id: undefined, name: 'name-1' }, 52 | { id: null, name: null }, 53 | ] 54 | const filteredItems = items.filter(arePropertiesNull('id', 'name')) 55 | 56 | assert.ok(filteredItems.length === 1) 57 | }) 58 | 59 | // arePropertiesNotNull --------------------------------------------------------------------------------------------- 60 | 61 | test(`arePropertiesNotNull id name`, () => { 62 | const items = [ 63 | { id: null, name: 'name-1' }, 64 | { id: 0, name: null }, 65 | { id: undefined, name: 'name-1' }, 66 | { id: null, name: null }, 67 | ] 68 | const filteredItems = items.filter(arePropertiesNotNull('id', 'name')) 69 | 70 | assert.ok(filteredItems.length === 1) 71 | }) 72 | 73 | test.run() 74 | -------------------------------------------------------------------------------- /src/isZero/isZero.test.ts: -------------------------------------------------------------------------------- 1 | import { suite } from 'uvu' 2 | import * as assert from 'uvu/assert' 3 | import { Everything } from '../types' 4 | import { isZero, isNotZero, isPropertyZero, isPropertyNotZero, arePropertiesNotZero, arePropertiesZero } from './isZero' 5 | 6 | const test = suite('zero') 7 | 8 | const zeroValues = [0] 9 | const notZeroValues = [-Infinity, -1, 1, Infinity, 'test', true, [], {}, undefined, null, '', false] 10 | 11 | // isZero ------------------------------------------------------------------------------------------------------------- 12 | 13 | zeroValues.forEach((value) => test(`isZero ${value}`, () => assert.ok(isZero(value)))) 14 | notZeroValues.forEach((value) => test(`! isZero ${value}`, () => assert.not(isZero(value)))) 15 | 16 | const _zeroType: 0[] = [...zeroValues, notZeroValues].filter(isZero) 17 | const _neverType: never[] = notZeroValues.filter(isZero) 18 | 19 | // isNotZero ---------------------------------------------------------------------------------------------------------- 20 | 21 | notZeroValues.forEach((value) => test(`isNotZero ${value}`, () => assert.ok(isNotZero(value)))) 22 | zeroValues.forEach((value) => test(`! isNotZero ${value}`, () => assert.not(isNotZero(value)))) 23 | 24 | const _allNotType: Exclude[] = [...zeroValues, ...notZeroValues].filter(isNotZero) 25 | const _neverNotType: number[] = zeroValues.filter(isNotZero) 26 | 27 | // isPropertyZero ----------------------------------------------------------------------------------------------------- 28 | 29 | test(`isPropertyZero id`, () => { 30 | const items = [{ id: 0 }, { id: undefined }, { id: null }] 31 | const filteredItems = items.filter(isPropertyZero('id')) 32 | 33 | assert.ok(filteredItems.length === 1) 34 | }) 35 | 36 | // isPropertyNotZero -------------------------------------------------------------------------------------------------- 37 | 38 | test(`isPropertyNotZero id`, () => { 39 | const items = [{ id: 0 }, { id: 1 }, { id: null }] 40 | const filteredItems = items.filter(isPropertyNotZero('id')) 41 | 42 | assert.ok(filteredItems.length === 2) 43 | }) 44 | 45 | // arePropertiesZero -------------------------------------------------------------------------------------------------- 46 | 47 | test(`arePropertiesZero id count`, () => { 48 | const items = [ 49 | { id: 0, count: 0 }, 50 | { id: undefined, count: 0 }, 51 | { id: 0, count: null }, 52 | { id: null, count: 12 }, 53 | ] 54 | const filteredItems = items.filter(arePropertiesZero('id', 'count')) 55 | 56 | assert.ok(filteredItems.length === 1) 57 | }) 58 | 59 | // arePropertiesNotZero ----------------------------------------------------------------------------------------------- 60 | 61 | test(`arePropertiesNotZero id count`, () => { 62 | const items = [ 63 | { id: 0, count: 0 }, 64 | { id: undefined, count: 0 }, 65 | { id: 0, count: null }, 66 | { id: null, count: 12 }, 67 | ] 68 | const filteredItems = items.filter(arePropertiesNotZero('id', 'count')) 69 | 70 | assert.ok(filteredItems.length === 1) 71 | }) 72 | 73 | test.run() 74 | -------------------------------------------------------------------------------- /src/isTruthy/isTruthy.test.ts: -------------------------------------------------------------------------------- 1 | import { suite } from 'uvu' 2 | import * as assert from 'uvu/assert' 3 | 4 | import { 5 | isTruthy, 6 | isFalsy, 7 | isPropertyTruthy, 8 | isPropertyFalsy, 9 | arePropertiesFalsy, 10 | arePropertiesTruthy, 11 | } from './isTruthy' 12 | 13 | const test = suite('truthy') 14 | 15 | const truthyValues = [-Infinity, -1, 1, Infinity, 'test', true, [], {}] 16 | const falsyValues = [undefined, null, 0, '', false] 17 | 18 | // isTruthy ----------------------------------------------------------------------------------------------------------- 19 | 20 | truthyValues.forEach((value) => test(`isTruthy ${value}`, () => assert.ok(isTruthy(value)))) 21 | falsyValues.forEach((value) => test(`! isTruthy ${value}`, () => assert.not(isTruthy(value)))) 22 | 23 | const _truthyType: {}[] = [...truthyValues, falsyValues].filter(isTruthy) 24 | const _notFalsyType: (string | number | true)[] = falsyValues.filter(isTruthy) 25 | 26 | // isFalsy ------------------------------------------------------------------------------------------------------------ 27 | 28 | falsyValues.forEach((value) => test(`isFalsy ${value}`, () => assert.ok(isFalsy(value)))) 29 | truthyValues.forEach((value) => test(`! isFalsy ${value}`, () => assert.not(isFalsy(value)))) 30 | 31 | // const _allNotType: (null | undefined | '' | 0 | false)[] = [...truthyValues, ...falsyValues].filter(isFalsy) 32 | const _neverNotType: never[] = truthyValues.filter(isFalsy) 33 | 34 | // isPropertyTruthy --------------------------------------------------------------------------------------------------- 35 | 36 | test(`isPropertyTruthy id`, () => { 37 | const items = [{ id: 0 }, { id: 1 }, { id: null }] 38 | const filteredItems = items.filter(isPropertyTruthy('id')) 39 | 40 | assert.ok(filteredItems.length === 1) 41 | }) 42 | 43 | // isPropertyFalsy ---------------------------------------------------------------------------------------------------- 44 | 45 | test(`isPropertyFalsy id`, () => { 46 | const items = [{ id: 0 }, { id: 1 }, { id: null }] 47 | const filteredItems = items.filter(isPropertyFalsy('id')) 48 | 49 | assert.ok(filteredItems.length === 2) 50 | }) 51 | 52 | // arePropertiesTruthy ------------------------------------------------------------------------------------------------ 53 | 54 | test(`arePropertiesTruthy id name`, () => { 55 | const items = [ 56 | { id: 0, name: 'name-1' }, 57 | { id: 1, name: '' }, 58 | { id: 1, name: 'name-1' }, 59 | { id: null, name: '' }, 60 | ] 61 | const filteredItems = items.filter(arePropertiesTruthy('id', 'name')) 62 | 63 | assert.ok(filteredItems.length === 1) 64 | }) 65 | 66 | // arePropertiesFalsy ------------------------------------------------------------------------------------------------- 67 | 68 | test(`arePropertiesFalsy id name`, () => { 69 | const items = [ 70 | { id: 0, name: 'name-1' }, 71 | { id: 1, name: '' }, 72 | { id: 1, name: 'name-1' }, 73 | { id: null, name: '' }, 74 | ] 75 | const filteredItems = items.filter(arePropertiesFalsy('id', 'name')) 76 | 77 | assert.ok(filteredItems.length === 1) 78 | }) 79 | 80 | test.run() 81 | -------------------------------------------------------------------------------- /src/isEmpty/isEmpty.test.ts: -------------------------------------------------------------------------------- 1 | import { suite } from 'uvu' 2 | import * as assert from 'uvu/assert' 3 | import { Everything } from '../types' 4 | 5 | import { 6 | arePropertiesEmpty, 7 | arePropertiesNotEmpty, 8 | isEmpty, 9 | isNotEmpty, 10 | isPropertyEmpty, 11 | isPropertyNotEmpty, 12 | } from './isEmpty' 13 | 14 | const test = suite('empty') 15 | 16 | const emptyValues = [''] 17 | const notEmptyValues = [-Infinity, -1, 1, Infinity, 'test', true, [], {}, undefined, null, 0, false] 18 | 19 | // isEmpty ------------------------------------------------------------------------------------------------------------ 20 | 21 | emptyValues.forEach((value) => test(`isEmpty ${value}`, () => assert.ok(isEmpty(value)))) 22 | notEmptyValues.forEach((value) => test(`! isEmpty ${value}`, () => assert.not(isEmpty(value)))) 23 | 24 | const _emptyType: ''[] = [...emptyValues, notEmptyValues].filter(isEmpty) 25 | const _neverType: never[] = notEmptyValues.filter(isEmpty) 26 | 27 | // isNotEmpty --------------------------------------------------------------------------------------------------------- 28 | 29 | notEmptyValues.forEach((value) => test(`isNotEmpty ${value}`, () => assert.ok(isNotEmpty(value)))) 30 | emptyValues.forEach((value) => test(`! isNotEmpty ${value}`, () => assert.not(isNotEmpty(value)))) 31 | 32 | const _allNotType: Exclude[] = [...emptyValues, ...notEmptyValues].filter(isNotEmpty) 33 | const _neverNotType: never[] = emptyValues.filter(isNotEmpty) 34 | 35 | // isPropertyEmpty ---------------------------------------------------------------------------------------------------- 36 | 37 | test(`isPropertyEmpty name`, () => { 38 | const items = [{ name: 'name-1' }, { name: '' }, { name: undefined }] 39 | const filteredItems = items.filter(isPropertyEmpty('name')) 40 | 41 | assert.ok(filteredItems.length === 1) 42 | }) 43 | 44 | // isPropertyNotEmpty ------------------------------------------------------------------------------------------------- 45 | 46 | test(`isPropertyNotEmpty name`, () => { 47 | const items = [{ name: null }, { name: 'name-2' }, { name: '' }] 48 | const filteredItems = items.filter(isPropertyNotEmpty('name')) 49 | 50 | assert.ok(filteredItems.length === 2) 51 | }) 52 | 53 | // arePropertiesEmpty ------------------------------------------------------------------------------------------------- 54 | 55 | test(`arePropertiesEmpty name role`, () => { 56 | const items = [ 57 | { name: 'name-1', role: 'ADMIN' }, 58 | { name: 'name-1', role: '' }, 59 | { name: '', role: '' }, 60 | { name: '', role: 'ADMIN' }, 61 | ] 62 | const filteredItems = items.filter(arePropertiesEmpty('name', 'role')) 63 | 64 | assert.ok(filteredItems.length === 1) 65 | }) 66 | 67 | // arePropertiesNotEmpty ---------------------------------------------------------------------------------------------- 68 | 69 | test(`arePropertiesEmpty name role`, () => { 70 | const items = [ 71 | { name: 'name-1', role: 'ADMIN' }, 72 | { name: 'name-1', role: '' }, 73 | { name: '', role: '' }, 74 | { name: '', role: 'ADMIN' }, 75 | ] 76 | const filteredItems = items.filter(arePropertiesNotEmpty('name', 'role')) 77 | 78 | assert.ok(filteredItems.length === 1) 79 | }) 80 | 81 | test.run() 82 | -------------------------------------------------------------------------------- /src/isTrue/isTrue.test.ts: -------------------------------------------------------------------------------- 1 | import { suite } from 'uvu' 2 | import * as assert from 'uvu/assert' 3 | import { Everything } from '../types' 4 | 5 | import { isTrue, isNotTrue, isPropertyNotTrue, isPropertyTrue, arePropertiesTrue, arePropertiesNotTrue } from './isTrue' 6 | 7 | const test = suite('true') 8 | 9 | const trueValues = [true] 10 | const notTrueValues = [-Infinity, -1, 1, Infinity, 'test', [], {}, undefined, null, 0, '', false] 11 | 12 | // isTrue ------------------------------------------------------------------------------------------------------------- 13 | 14 | trueValues.forEach((value) => test(`isTrue ${value}`, () => assert.ok(isTrue(value)))) 15 | notTrueValues.forEach((value) => test(`! isTrue ${value}`, () => assert.not(isTrue(value)))) 16 | 17 | const _trueType: true[] = [...trueValues, notTrueValues].filter(isTrue) 18 | const _neverType: never[] = notTrueValues.filter(isTrue) 19 | 20 | // isNotTrue ---------------------------------------------------------------------------------------------------------- 21 | 22 | notTrueValues.forEach((value) => test(`isNotTrue ${value}`, () => assert.ok(isNotTrue(value)))) 23 | trueValues.forEach((value) => test(`! isNotTrue ${value}`, () => assert.not(isNotTrue(value)))) 24 | 25 | const _allNotType: Exclude[] = [...trueValues, ...notTrueValues].filter(isNotTrue) 26 | // const _neverNotType: never[] = trueValues.filter(isNotTrue) 27 | 28 | // isPropertyTrue ----------------------------------------------------------------------------------------------------- 29 | 30 | test(`isPropertyTrue available`, () => { 31 | const items = [{ available: true }, { available: 'some-id' }, { available: null }] 32 | const filteredItems = items.filter(isPropertyTrue('available')) 33 | 34 | assert.ok(filteredItems.length === 1) 35 | }) 36 | 37 | // isPropertyNotTrue -------------------------------------------------------------------------------------------------- 38 | 39 | test(`isPropertyNotTrue available`, () => { 40 | const items = [{ available: 0 }, { available: true }, { available: null }] 41 | const filteredItems = items.filter(isPropertyNotTrue('available')) 42 | 43 | assert.ok(filteredItems.length === 2) 44 | }) 45 | 46 | // arePropertiesTrue -------------------------------------------------------------------------------------------------- 47 | 48 | test(`arePropertiesTrue available inStock`, () => { 49 | const items = [ 50 | { available: 'some-id', inStock: true }, 51 | { available: true, inStock: true }, 52 | { available: 'some-id', inStock: false }, 53 | { available: null, inStock: true }, 54 | ] 55 | const filteredItems = items.filter(arePropertiesTrue('available', 'inStock')) 56 | 57 | assert.ok(filteredItems.length === 1) 58 | }) 59 | 60 | // arePropertiesNotTrue ----------------------------------------------------------------------------------------------- 61 | 62 | test(`arePropertiesNotTrue available inStock`, () => { 63 | const items = [ 64 | { available: 'some-id', inStock: true }, 65 | { available: true, inStock: true }, 66 | { available: 'some-id', inStock: false }, 67 | { available: null, inStock: true }, 68 | ] 69 | const filteredItems = items.filter(arePropertiesNotTrue('available', 'inStock')) 70 | 71 | assert.ok(filteredItems.length === 1) 72 | }) 73 | 74 | test.run() 75 | -------------------------------------------------------------------------------- /src/isUndefined/isUndefined.test.ts: -------------------------------------------------------------------------------- 1 | import { suite } from 'uvu' 2 | import * as assert from 'uvu/assert' 3 | 4 | import { 5 | isUndefined, 6 | isNotUndefined, 7 | isPropertyUndefined, 8 | isPropertyNotUndefined, 9 | arePropertiesUndefined, 10 | arePropertiesNotUndefined, 11 | } from './isUndefined' 12 | 13 | const test = suite('undefined') 14 | 15 | const undefinedValues: undefined[] = [undefined] 16 | const notUndefinedValues = [-Infinity, -1, 1, Infinity, 'test', true, [], {}, null, 0, '', false] 17 | 18 | // isUndefined -------------------------------------------------------------------------------------------------------- 19 | 20 | undefinedValues.forEach((value) => test(`isUndefined ${value}`, () => assert.ok(isUndefined(value)))) 21 | notUndefinedValues.forEach((value) => test(`! isUndefined ${value}`, () => assert.not(isUndefined(value)))) 22 | 23 | const _undefinedType: undefined[] = [...undefinedValues, notUndefinedValues].filter(isUndefined) 24 | const _neverType: never[] = notUndefinedValues.filter(isUndefined) 25 | 26 | // isNotUndefined ----------------------------------------------------------------------------------------------------- 27 | 28 | notUndefinedValues.forEach((value) => test(`isNotUndefined ${value}`, () => assert.ok(isNotUndefined(value)))) 29 | undefinedValues.forEach((value) => test(`! isNotUndefined ${value}`, () => assert.not(isNotUndefined(value)))) 30 | 31 | const _allNotType: ({} | null)[] = [...undefinedValues, ...notUndefinedValues].filter(isNotUndefined) 32 | const _neverNotType: never[] = undefinedValues.filter(isNotUndefined) 33 | 34 | // isPropertyUndefined ------------------------------------------------------------------------------------------------ 35 | 36 | test(`isPropertyUndefined id`, () => { 37 | const items = [{ id: 0 }, { id: undefined }, { id: null }] 38 | const filteredItems = items.filter(isPropertyUndefined('id')) 39 | 40 | assert.ok(filteredItems.length === 1) 41 | }) 42 | 43 | // isPropertyNotUndefined --------------------------------------------------------------------------------------------- 44 | 45 | test(`isPropertyNotUndefined id`, () => { 46 | const items = [{ id: undefined }, { id: 1 }, { id: null }] 47 | const filteredItems = items.filter(isPropertyNotUndefined('id')) 48 | 49 | assert.ok(filteredItems.length === 2) 50 | }) 51 | 52 | // arePropertiesUndefined --------------------------------------------------------------------------------------------- 53 | 54 | test(`arePropertiesUndefined id name`, () => { 55 | const items = [ 56 | { id: 0, name: undefined }, 57 | { id: 12, name: 'name-1' }, 58 | { id: undefined, name: undefined }, 59 | { id: null, name: undefined }, 60 | ] 61 | const filteredItems = items.filter(arePropertiesUndefined('id', 'name')) 62 | 63 | assert.ok(filteredItems.length === 1) 64 | }) 65 | 66 | // arePropertiesNotUndefined ------------------------------------------------------------------------------------------ 67 | 68 | test(`arePropertiesNotUndefined id name`, () => { 69 | const items = [ 70 | { id: 0, name: undefined }, 71 | { id: 12, name: 'name-1' }, 72 | { id: undefined, name: undefined }, 73 | { id: null, name: undefined }, 74 | ] 75 | const filteredItems = items.filter(arePropertiesNotUndefined('id', 'name')) 76 | 77 | assert.ok(filteredItems.length === 1) 78 | }) 79 | 80 | test.run() 81 | -------------------------------------------------------------------------------- /src/isFalse/isFalse.test.ts: -------------------------------------------------------------------------------- 1 | import { suite } from 'uvu' 2 | import * as assert from 'uvu/assert' 3 | import { Everything } from '../types' 4 | 5 | import { 6 | arePropertiesFalse, 7 | arePropertiesNotFalse, 8 | isFalse, 9 | isNotFalse, 10 | isPropertyFalse, 11 | isPropertyNotFalse, 12 | } from './isFalse' 13 | 14 | const test = suite('false') 15 | 16 | const falseValues = [false] 17 | const notFalseValues = [-Infinity, -1, 1, Infinity, 'test', true, [], {}, undefined, null, 0, ''] 18 | 19 | // isFalse ------------------------------------------------------------------------------------------------------------ 20 | 21 | falseValues.forEach((value) => test(`isFalse ${value}`, () => assert.ok(isFalse(value)))) 22 | notFalseValues.forEach((value) => test(`! isFalse ${value}`, () => assert.not(isFalse(value)))) 23 | 24 | const _falseType: false[] = [...falseValues, notFalseValues].filter(isFalse) 25 | const _neverType: never[] = notFalseValues.filter(isFalse) 26 | 27 | // isNotFalse --------------------------------------------------------------------------------------------------------- 28 | 29 | notFalseValues.forEach((value) => test(`isNotFalse ${value}`, () => assert.ok(isNotFalse(value)))) 30 | falseValues.forEach((value) => test(`! isNotFalse ${value}`, () => assert.not(isNotFalse(value)))) 31 | 32 | const _allNotType: Exclude[] = [...falseValues, ...notFalseValues].filter(isNotFalse) 33 | // const _neverNotType: never[] = falseValues.filter(isNotFalse) 34 | 35 | // isPropertyFalse ---------------------------------------------------------------------------------------------------- 36 | 37 | test(`isPropertyTrue available`, () => { 38 | const items = [{ available: false }, { available: '' }, { available: undefined }] 39 | const filteredItems = items.filter(isPropertyFalse('available')) 40 | 41 | assert.ok(filteredItems.length === 1) 42 | }) 43 | 44 | // isPropertyNotFalse ------------------------------------------------------------------------------------------------- 45 | 46 | test(`isPropertyNotFalse available`, () => { 47 | const items = [{ available: false }, { available: true }, { available: null }] 48 | const filteredItems = items.filter(isPropertyNotFalse('available')) 49 | 50 | assert.ok(filteredItems.length === 2) 51 | }) 52 | 53 | // arePropertiesFalse ------------------------------------------------------------------------------------------------- 54 | 55 | test(`arePropertiesFalse available inStock`, () => { 56 | const items = [ 57 | { available: true, inStock: false }, 58 | { available: false, inStock: false }, 59 | { available: true, inStock: true }, 60 | { available: null, inStock: false }, 61 | ] 62 | const filteredItems = items.filter(arePropertiesFalse('available', 'inStock')) 63 | 64 | assert.ok(filteredItems.length === 1) 65 | }) 66 | 67 | // arePropertiesNotFalse ---------------------------------------------------------------------------------------------- 68 | 69 | test(`arePropertiesNotFalse available inStock`, () => { 70 | const items = [ 71 | { available: true, inStock: false }, 72 | { available: false, inStock: false }, 73 | { available: true, inStock: true }, 74 | { available: null, inStock: true }, 75 | ] 76 | const filteredItems = items.filter(arePropertiesNotFalse('available', 'inStock')) 77 | 78 | assert.ok(filteredItems.length === 2) 79 | }) 80 | 81 | test.run() 82 | -------------------------------------------------------------------------------- /src/sorting/sorting.test.ts: -------------------------------------------------------------------------------- 1 | import { suite } from 'uvu' 2 | import * as assert from 'uvu/assert' 3 | import { 4 | sortDateASC, 5 | sortDateDESC, 6 | sortDatePropertyASC, 7 | sortDatePropertyDESC, 8 | sortNumberASC, 9 | sortNumberDESC, 10 | sortNumberPropertyASC, 11 | sortNumberPropertyDESC, 12 | sortStringASC, 13 | sortStringPropertyASC, 14 | sortStringPropertyDESC, 15 | } from './sorting' 16 | 17 | const test = suite('sorting') 18 | 19 | // number ------------------------------------------------------------------------------------------------------------- 20 | 21 | test(`sortNumberASC`, () => { 22 | const items = [123, null, 0, -10] 23 | const sortedItems = items.sort(sortNumberASC) 24 | 25 | assert.equal(sortedItems, [null, -10, 0, 123]) 26 | }) 27 | 28 | test(`sortNumberDESC`, () => { 29 | const items = [12, -3, 0] 30 | const sortedItems = items.sort(sortNumberDESC) 31 | 32 | assert.equal(sortedItems, [12, 0, -3]) 33 | }) 34 | 35 | test(`sortNumberPropertyASC`, () => { 36 | const items = [{ a: 12 }, { a: -3 }] 37 | const sortedItems = items.sort(sortNumberPropertyASC('a')) 38 | 39 | assert.equal(sortedItems, [{ a: -3 }, { a: 12 }]) 40 | }) 41 | 42 | test(`sortNumberPropertyDESC`, () => { 43 | const items = [{ t: 0 }, { t: 100 }] 44 | const sortedItems = items.sort(sortNumberPropertyDESC('t')) 45 | 46 | assert.equal(sortedItems, [{ t: 100 }, { t: 0 }]) 47 | }) 48 | 49 | // string ------------------------------------------------------------------------------------------------------------- 50 | 51 | test(`sortStringASC`, () => { 52 | const items = ['z', null, 'A', ''] 53 | const sortedItems = items.sort(sortStringASC) 54 | 55 | assert.equal(sortedItems, [null, '', 'A', 'z']) 56 | }) 57 | 58 | test(`sortStringDESC`, () => { 59 | const items = ['z', 'B', 'a'] 60 | const sortedItems = items.sort(sortStringASC) 61 | 62 | assert.equal(sortedItems, ['a', 'B', 'z']) 63 | }) 64 | 65 | test(`sortStringASC Upper-/Lowercase`, () => { 66 | const items = ['hi', 'hI', 'Hi', 'HI'] 67 | const sortedItems = items.sort(sortStringASC) 68 | assert.equal(sortedItems, items) 69 | 70 | const items2 = ['HI', 'hI', 'Hi', 'hi'] 71 | const sortedItems2 = items2.sort(sortStringASC) 72 | assert.equal(sortedItems2, items2) 73 | }) 74 | 75 | test(`sortStringPropertyASC`, () => { 76 | const items = [{ a: 'z' }, { a: 'b' }] 77 | const sortedItems = items.sort(sortStringPropertyASC('a')) 78 | 79 | assert.equal(sortedItems, [{ a: 'b' }, { a: 'z' }]) 80 | }) 81 | 82 | test(`sortStringPropertyDESC`, () => { 83 | const items = [{ t: 'a' }, { t: 'c' }] 84 | const sortedItems = items.sort(sortStringPropertyDESC('t')) 85 | 86 | assert.equal(sortedItems, [{ t: 'c' }, { t: 'a' }]) 87 | }) 88 | 89 | // date --------------------------------------------------------------------------------------------------------------- 90 | 91 | const today = new Date() 92 | const tomorrow = new Date(today.getTime() + 24 * 60 * 60 * 1000) 93 | test(`sortDateASC`, () => { 94 | const items = [tomorrow, null, today] 95 | const sortedItems = items.sort(sortDateASC) 96 | 97 | assert.equal(sortedItems, [null, today, tomorrow]) 98 | }) 99 | 100 | test(`sortDateDESC`, () => { 101 | const items = [today, tomorrow, undefined] 102 | const sortedItems = items.sort(sortDateDESC) 103 | 104 | assert.equal(sortedItems, [tomorrow, today, undefined]) 105 | }) 106 | 107 | test(`sortDatePropertyASC`, () => { 108 | const items = [{ a: tomorrow }, { a: today }] 109 | const sortedItems = items.sort(sortDatePropertyASC('a')) 110 | 111 | assert.equal(sortedItems, [{ a: today }, { a: tomorrow }]) 112 | }) 113 | 114 | test(`sortDatePropertyDESC`, () => { 115 | const items = [{ t: today }, { t: tomorrow }] 116 | const sortedItems = items.sort(sortDatePropertyDESC('t')) 117 | 118 | assert.equal(sortedItems, [{ t: tomorrow }, { t: today }]) 119 | }) 120 | 121 | test.run() 122 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | A collection of a few small lightweight typesafe utilities. 2 | 3 | ## Motivation 4 | 5 | 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. 6 | 7 | In this collection you will find some useful functions that are fully typed. 8 | 9 | ## Install 10 | 11 | ``` 12 | $ npm install --save-dev typesafe-utils 13 | ``` 14 | 15 | ## Overview 16 | - [filter functions](#filter functions) 17 | - is 18 | - [is](#is) 19 | - [isNot](#isNot) 20 | - [isProperty](#isProperty) 21 | - [isPropertyNot](#isPropertyNot) 22 | - isTruthy 23 | - [isTruthy](#isTruthy) 24 | - [isFalsy](#isFalsy) 25 | - [isPropertyTruthy](#isPropertyTruthy) 26 | - [isPropertyFalsy](#isPropertyFalsy) 27 | - [arePropertiesTruthy](#arePropertiesTruthy) 28 | - [arePropertiesFalsy](#arePropertiesFalsy) 29 | - isUndefined 30 | - [isUndefined](#isUndefined) 31 | - [isNotUndefined](#isNotUndefined) 32 | - [isPropertyUndefined](#isPropertyUndefined) 33 | - [isPropertyNotUndefined](#isPropertyNotUndefined) 34 | - [arePropertiesUndefined](#arePropertiesUndefined) 35 | - [arePropertiesNotUndefined](#arePropertiesNotUndefined) 36 | - isNull 37 | - [isNull](#isNull) 38 | - [isNotNull](#isNotNull) 39 | - [isPropertyNull](#isPropertyNull) 40 | - [isPropertyNotNull](#isPropertyNotNull) 41 | - [arePropertiesNull](#arePropertiesNull) 42 | - [arePropertiesNotNull](#arePropertiesNotNull) 43 | - boolean 44 | - [isBoolean](#isBoolean) 45 | - isTrue 46 | - [isTrue](#isTrue) 47 | - [isNotTrue](#isNotTrue)* 48 | - [isPropertyTrue](#isPropertyTrue) 49 | - [isPropertyNotTrue](#isPropertyNotTrue)* 50 | - [arePropertiesTrue](#arePropertiesTrue) 51 | - [arePropertiesNotTrue](#arePropertiesNotTrue)* 52 | - isFalse 53 | - [isFalse](#isFalse) 54 | - [isNotFalse](#isNotFalse)* 55 | - [isPropertyFalse](#isPropertyFalse) 56 | - [isPropertyNotFalse](#isPropertyNotFalse)* 57 | - [arePropertiesFalse](#arePropertiesFalse) 58 | - [arePropertiesNotFalse](#arePropertiesNotFalse)* 59 | - number 60 | - [isNumber](#isNumber) 61 | - isZero 62 | - [isZero](#isZero) 63 | - [isNotZero](#isNotZero)* 64 | - [isPropertyZero](#isPropertyZero) 65 | - [isPropertyNotZero](#isPropertyNotZero)* 66 | - [arePropertiesZero](#arePropertiesZero) 67 | - [arePropertiesNotZero](#arePropertiesNotZero)* 68 | - string 69 | - [isString](#isString) 70 | - isEmpty 71 | - [isEmpty](#isEmpty) 72 | - [isNotEmpty](#isNotEmpty) 73 | - [isPropertyEmpty](#isPropertyEmpty) 74 | - [isPropertyNotEmpty](#isPropertyNotEmpty) 75 | - [arePropertiesEmpty](#arePropertiesEmpty) 76 | - [arePropertiesNotEmpty](#arePropertiesNotEmpty) 77 | - array 78 | - [isArray](#isArray) 79 | - [isArrayNotEmpty](#isArrayNotEmpty) 80 | - [isArrayEmpty](#isArrayEmpty) 81 | - object 82 | - [isObject](#isObject) 83 | - [isPrimitiveObject](#isPrimitiveObject) 84 | - duplicates 85 | - [filterDuplicates](#filterDuplicates) 86 | - [filterDuplicatesByKey](#filterDuplicatesByKey) 87 | - logical operators 88 | - [and](#and)* 89 | - [or](#or)* 90 | - [not](#not)* 91 | - typeguards 92 | - [createFilter](#TypeGuardInverted)* 93 | 94 | - [sorting functions](#sorting functions) 95 | - number 96 | - [sortNumberASC](#sortNumberASC) 97 | - [sortNumberDESC](#sortNumberDESC) 98 | - [sortNumberPropertyASC](#sortNumberPropertyASC) 99 | - [sortNumberPropertyDESC](#sortNumberPropertyDESC) 100 | - string 101 | - [sortStringASC](#sortStringASC) 102 | - [sortStringDESC](#sortStringDESC) 103 | - [sortStringPropertyASC](#sortStringPropertyASC) 104 | - [sortStringPropertyDESC](#sortStringPropertyDESC) 105 | - date 106 | - [sortDateASC](#sortDateASC) 107 | - [sortDateDESC](#sortDateDESC) 108 | - [sortDatePropertyASC](#sortDatePropertyASC) 109 | - [sortDatePropertyDESC](#sortDatePropertyDESC) 110 | 111 | - [mapping functions](#mapping functions) 112 | - [pick](#pick) 113 | 114 | - [other](#other) 115 | - [deepClone](#deepClone) 116 | - [uniqueArray](#uniqueArray) 117 | 118 | - [types](#types) 119 | - [Truthy](#truthy) 120 | - [Falsy](#falsy) 121 | - [TypeGuard](#typeguard) 122 | - [TypeGuardWithReturnType](#typeguardwithreturntype) 123 | - [TypeGuardInverted](#typeguardinverted) 124 | - [TypeGuardInvertedWithReturnType](#typeguardinvertedwithreturntype) 125 | 126 | | * not automatically 100% typesafe. It's better than nothing but to be 100% typesafe you need to pass generics yourself. 127 | 128 | ## filter functions 129 | 130 | A bunch of utilities that return true or false. Useful for array filter functions. 131 | 132 | > 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. 133 | 134 | ```TypeScript 135 | // BASIC example -------------------------------------------------------------- 136 | 137 | const result = [true, false].filter(bool => !!bool) 138 | // => result: boolean[] => [true] 139 | 140 | 141 | import { isTruthy } from 'typesafe-utils' 142 | const typesafeResult = [true, false].filter(isTruthy) 143 | // => typesafeResult: true[] => [true] 144 | 145 | 146 | // ADVANCED example ----------------------------------------------------------- 147 | 148 | const result = ['text', null, 'another text', undefined].filter(value => value !== '') 149 | // => result: (string | null | undefined)[] => ['text', 'another text'] 150 | 151 | 152 | import { isNotEmpty } from 'typesafe-utils' 153 | const typesafeResult = ['text', null, 'another text', undefined].filter(isNotEmpty) 154 | // => typesafeResult: string[] => ['text', 'another text'] 155 | ``` 156 | 157 | 158 | 159 | ### is 160 | 161 | returns `true` iff value is equals to the property you pass to the function 162 | 163 | #### Usage 164 | ```TypeScript 165 | import { is } from 'typesafe-utils' 166 | 167 | const result = [1, 15, 10, 43].filter(is(10)) 168 | // result: number[] => [10] 169 | ``` 170 | 171 | ### isNot 172 | 173 | returns `true` iff value is **not** equal to the property you pass to the function 174 | 175 | #### Usage 176 | ```TypeScript 177 | import { isNot } from 'typesafe-utils' 178 | 179 | const result = ['text', 'forbidden', 'blabla'].filter(isNot('forbidden')) 180 | // result: string[] => ['text', 'blabla'] 181 | ``` 182 | 183 | ### isProperty 184 | 185 | returns `true` iff the attribute of the object equals the property you pass to the function 186 | 187 | #### Usage 188 | ```TypeScript 189 | import { isProperty } from 'typesafe-utils' 190 | 191 | type Product = { 192 | id: number 193 | } 194 | 195 | const items: Product[] = [ 196 | { id: 1 }, 197 | { id: 3 } 198 | ] 199 | const result = items.filter(isProperty('id', 3)) 200 | // result: Product[] => [{ id: 3 }] 201 | ``` 202 | 203 | ### isPropertyNot 204 | 205 | returns `true` iff the attribute of the object is **not** equal to the property you pass to the function 206 | 207 | #### Usage 208 | ```TypeScript 209 | import { isPropertyNot } from 'typesafe-utils' 210 | 211 | type Product = { 212 | id: number 213 | } 214 | 215 | const items: Product[] = [ 216 | { id: 156 }, 217 | { id: 123 } 218 | ] 219 | const result = items.filter(isPropertyNot('id', 123)) 220 | // result: Product[] => [{ id: 156 }] 221 | ``` 222 | 223 | 224 | 225 | ### isTruthy 226 | 227 | returns `true` iff value is **not** `false | '' | 0 | null | undefined` 228 | 229 | #### Usage 230 | ```TypeScript 231 | import { isTruthy } from 'typesafe-utils' 232 | 233 | const result = [true, false, undefined, null].filter(isTruthy) 234 | // result: true[] => [true] 235 | ``` 236 | 237 | ### isFalsy 238 | 239 | returns `true` iff value is `false | '' | 0 | null | undefined` 240 | 241 | #### Usage 242 | ```TypeScript 243 | import { isFalsy } from 'typesafe-utils' 244 | 245 | const result = [true, false, 'text', 123, null].filter(isFalsy) 246 | // result: (false | null)[] => [false, null] 247 | ``` 248 | 249 | ### isPropertyTruthy 250 | 251 | returns `true` iff the attribute of the object is truthy 252 | 253 | #### Usage 254 | ```TypeScript 255 | import { isPropertyTruthy } from 'typesafe-utils' 256 | 257 | type Product = { 258 | id: number 259 | } 260 | 261 | const items: Product[] = [ 262 | { id: 1 }, 263 | { id: null }, 264 | { id: undefined } 265 | ] 266 | const result = items.filter(isPropertyTruthy('id')) 267 | // result: Product[] => [{ id: 1 }] 268 | ``` 269 | 270 | ### isPropertyFalsy 271 | 272 | returns `true` iff the attribute of the object is falsy 273 | 274 | #### Usage 275 | ```TypeScript 276 | import { isPropertyFalsy } from 'typesafe-utils' 277 | 278 | type Product = { 279 | id: number 280 | } 281 | 282 | const items: Product[] = [ 283 | { id: 5 }, 284 | { id: null } 285 | ] 286 | const result = items.filter(isPropertyFalsy('id')) 287 | // result: Product[] => [{ id: null }] 288 | ``` 289 | 290 | ### arePropertiesTruthy 291 | 292 | returns `true` iff all attributes of the object are truthy 293 | 294 | #### Usage 295 | ```TypeScript 296 | import { arePropertiesTruthy } from 'typesafe-utils' 297 | 298 | type Product = { 299 | id: number 300 | name: string 301 | } 302 | 303 | const items: Product[] = [ ... ] 304 | 305 | const result = items.filter(arePropertiesTruthy('id', 'name')) 306 | ``` 307 | 308 | ### arePropertiesFalsy 309 | 310 | returns `true` iff all attributes of the object are falsy 311 | 312 | #### Usage 313 | ```TypeScript 314 | import { arePropertiesFalsy } from 'typesafe-utils' 315 | 316 | type Product = { 317 | id: number 318 | name: string 319 | } 320 | 321 | const items: Product[] = [ ... ] 322 | 323 | const result = items.filter(arePropertiesFalsy('id', 'name')) 324 | ``` 325 | 326 | 327 | 328 | ### isUndefined 329 | 330 | returns `true` iff value is `undefined` 331 | 332 | #### Usage 333 | ```TypeScript 334 | import { isUndefined } from 'typesafe-utils' 335 | 336 | const result = [undefined, null, true].filter(isUndefined) 337 | // result: undefined[] => [undefined] 338 | ``` 339 | 340 | ### isNotUndefined 341 | 342 | returns `true` iff value is **not** `undefined` 343 | 344 | #### Usage 345 | ```TypeScript 346 | import { isNotUndefined } from 'typesafe-utils' 347 | 348 | const result = [null, undefined].filter(isNotUndefined) 349 | // result: null[] => [null] 350 | ``` 351 | 352 | ### isPropertyUndefined 353 | 354 | returns `true` iff the attribute of the object is `undefined` 355 | 356 | #### Usage 357 | ```TypeScript 358 | import { isPropertyUndefined } from 'typesafe-utils' 359 | 360 | type Product = { 361 | id: number | undefined 362 | } 363 | 364 | const items: Product[] = [ 365 | { id: 1 }, 366 | { id: undefined } 367 | ] 368 | const result = items.filter(isPropertyUndefined('id')) 369 | // result: Product[] => [{ id: undefined }] 370 | ``` 371 | 372 | ### isPropertyNotUndefined 373 | 374 | returns `true` iff the attribute of the object is **not** `undefined` 375 | 376 | #### Usage 377 | ```TypeScript 378 | import { isPropertyNotUndefined } from 'typesafe-utils' 379 | 380 | type Product = { 381 | id: number 382 | } 383 | 384 | const items: Product[] = [ 385 | { id: 5 }, 386 | { id: undefined } 387 | ] 388 | const result = items.filter(isPropertyNotUndefined('id')) 389 | // result: Product[] => [{ id: 5 }] 390 | ``` 391 | 392 | ### arePropertiesUndefined 393 | 394 | returns `true` iff all attributes of the object are `undefined` 395 | 396 | #### Usage 397 | ```TypeScript 398 | import { arePropertiesUndefined } from 'typesafe-utils' 399 | 400 | type Product = { 401 | id: number 402 | name: string 403 | } 404 | 405 | const items: Product[] = [ ... ] 406 | 407 | const result = items.filter(arePropertiesUndefined('id', 'name')) 408 | ``` 409 | 410 | ### arePropertiesNotUndefined 411 | 412 | returns `true` iff all attributes of the object are **not** `undefined` 413 | 414 | #### Usage 415 | ```TypeScript 416 | import { arePropertiesNotUndefined } from 'typesafe-utils' 417 | 418 | type Product = { 419 | id: number 420 | name: string 421 | } 422 | 423 | const items: Product[] = [ ... ] 424 | 425 | const result = items.filter(arePropertiesNotUndefined('id', 'name')) 426 | ``` 427 | 428 | 429 | 430 | ### isNull 431 | 432 | returns `true` iff value is `null` 433 | 434 | #### Usage 435 | ```TypeScript 436 | import { isNull } from 'typesafe-utils' 437 | 438 | const result = [null, undefined].filter(isNull) 439 | // result: null[] => [null] 440 | ``` 441 | 442 | ### isNotNull 443 | 444 | returns `true` iff value is **not** `null` 445 | 446 | #### Usage 447 | ```TypeScript 448 | import { isNotNull } from 'typesafe-utils' 449 | 450 | const result = [false, null].filter(isNotNull) 451 | // result: boolean[] => [false] 452 | ``` 453 | 454 | ### isPropertyNull 455 | 456 | returns `true` iff the attribute of the object is `null` 457 | 458 | #### Usage 459 | ```TypeScript 460 | import { isPropertyNull } from 'typesafe-utils' 461 | 462 | type Product = { 463 | id: number | null 464 | } 465 | 466 | const items: Product[] = [ 467 | { id: 0 }, 468 | { id: null } 469 | ] 470 | const result = items.filter(isPropertyNull('id')) 471 | // result: Product[] => [{ id: null }] 472 | ``` 473 | 474 | ### isPropertyNotNull 475 | 476 | returns `true` iff the attribute of the object is **not** `null` 477 | 478 | #### Usage 479 | ```TypeScript 480 | import { isPropertyNotNull } from 'typesafe-utils' 481 | 482 | type Product = { 483 | id: number 484 | } 485 | 486 | const items: Product[] = [ 487 | { id: 5 }, 488 | { id: null } 489 | ] 490 | const result = items.filter(isPropertyNotNull('id')) 491 | // result: Product[] => [{ id: 5 }] 492 | ``` 493 | 494 | ### arePropertiesNull 495 | 496 | returns `true` iff all attributes of the object are `null` 497 | 498 | #### Usage 499 | ```TypeScript 500 | import { arePropertiesNull } from 'typesafe-utils' 501 | 502 | type Product = { 503 | id: number 504 | name: string 505 | } 506 | 507 | const items: Product[] = [ ... ] 508 | 509 | const result = items.filter(arePropertiesNull('id', 'name')) 510 | ``` 511 | 512 | ### arePropertiesNotNull 513 | 514 | returns `true` iff all attributes of the object are **not** `null` 515 | 516 | #### Usage 517 | ```TypeScript 518 | import { arePropertiesNotNull } from 'typesafe-utils' 519 | 520 | type Product = { 521 | id: number 522 | name: string 523 | } 524 | 525 | const items: Product[] = [ ... ] 526 | 527 | const result = items.filter(arePropertiesNotNull('id', 'name')) 528 | ``` 529 | 530 | 531 | 532 | ### isBoolean 533 | 534 | returns `true` iff value is of type `boolean` 535 | 536 | #### Usage 537 | ```TypeScript 538 | import { isBoolean } from 'typesafe-utils' 539 | 540 | const result = [true, 'some text', 1, false].filter(isBoolean) 541 | // result: boolean[] => [true, false] 542 | ``` 543 | 544 | ### isTrue 545 | 546 | returns `true` iff value is `true` 547 | 548 | #### Usage 549 | ```TypeScript 550 | import { isTrue } from 'typesafe-utils' 551 | 552 | const result = [true, 'some text', 1].filter(isTrue) 553 | // result: true[] => [true] 554 | ``` 555 | 556 | ### isNotTrue 557 | 558 | returns `true` iff value is **not** `true` 559 | 560 | > Note: it is currently **not** possible to make this function fully typesafe.\ 561 | > `[true, 123].filter(isNotTrue)` will have the type `(false | number)[]` 562 | 563 | #### Usage 564 | ```TypeScript 565 | import { isNotTrue } from 'typesafe-utils' 566 | 567 | const result = [true, false].filter(isNotTrue) 568 | // result: false[] => [false] 569 | ``` 570 | 571 | ### isPropertyTrue 572 | 573 | returns `true` iff the attribute of the object is `true` 574 | 575 | #### Usage 576 | ```TypeScript 577 | import { isPropertyTrue } from 'typesafe-utils' 578 | 579 | type Product = { 580 | available: boolean | null 581 | } 582 | 583 | const items: Product[] = [ 584 | { available: true }, 585 | { available: null } 586 | ] 587 | const result = items.filter(isPropertyTrue('available')) 588 | // result: Product[] => [{ available: true }] 589 | ``` 590 | 591 | ### isPropertyNotTrue 592 | 593 | returns `true` iff the attribute of the object is **not** `true` 594 | 595 | #### Usage 596 | ```TypeScript 597 | import { isPropertyNotTrue } from 'typesafe-utils' 598 | 599 | type Product = { 600 | id: number 601 | } 602 | 603 | const items: Product[] = [ 604 | { available: true }, 605 | { available: false } 606 | ] 607 | const result = items.filter(isPropertyNotTrue('available')) 608 | // result: Product[] => [{ available: false }] 609 | ``` 610 | 611 | ### arePropertiesTrue 612 | 613 | returns `true` iff all attributes of the object are `true` 614 | 615 | #### Usage 616 | ```TypeScript 617 | import { arePropertiesTrue } from 'typesafe-utils' 618 | 619 | type Product = { 620 | count: number 621 | available: string 622 | } 623 | 624 | const items: Product[] = [ ... ] 625 | 626 | const result = items.filter(arePropertiesTrue('count', 'available')) 627 | ``` 628 | 629 | ### arePropertiesNotTrue 630 | 631 | returns `true` iff all attributes of the object are **not** `true` 632 | 633 | #### Usage 634 | ```TypeScript 635 | import { arePropertiesNotTrue } from 'typesafe-utils' 636 | 637 | type Product = { 638 | count: number 639 | available: string 640 | } 641 | 642 | const items: Product[] = [ ... ] 643 | 644 | const result = items.filter(arePropertiesNotTrue('count', 'available')) 645 | ``` 646 | 647 | 648 | 649 | ### isFalse 650 | 651 | returns `true` iff value is `false` 652 | 653 | #### Usage 654 | ```TypeScript 655 | import { isFalse } from 'typesafe-utils' 656 | 657 | const result = [0, false, undefined].filter(isFalse) 658 | // result: false[] => [false] 659 | ``` 660 | 661 | ### isNotFalse 662 | 663 | returns `true` iff value is **not** `false` 664 | 665 | > Note: it is currently **not** possible to make this function fully typesafe.\ 666 | > `[false, 123].filter(isNotFalse)` will have the type `(true | number)[]` 667 | 668 | #### Usage 669 | ```TypeScript 670 | import { isNotFalse } from 'typesafe-utils' 671 | 672 | const result = [false, null].filter(isNotFalse) 673 | // result: null[] => [null] 674 | ``` 675 | 676 | ### isPropertyFalse 677 | 678 | returns `true` iff the attribute of the object is `false` 679 | 680 | #### Usage 681 | ```TypeScript 682 | import { isPropertyFalse } from 'typesafe-utils' 683 | 684 | type Product = { 685 | available: boolean | null 686 | } 687 | 688 | const items: Product[] = [ 689 | { available: false }, 690 | { available: true }, 691 | { available: null } 692 | ] 693 | const result = items.filter(isPropertyFalse('available')) 694 | // result: Product[] => [{ available: false }] 695 | ``` 696 | 697 | ### isPropertyNotFalse 698 | 699 | returns `true` iff the attribute of the object is **not** `false` 700 | 701 | #### Usage 702 | ```TypeScript 703 | import { isPropertyNotFalse } from 'typesafe-utils' 704 | 705 | type Product = { 706 | id: number 707 | } 708 | 709 | const items: Product[] = [ 710 | { available: true }, 711 | { available: false } 712 | ] 713 | const result = items.filter(isPropertyNotFalse('available')) 714 | // result: Product[] => [{ available: true }] 715 | ``` 716 | 717 | ### arePropertiesFalse 718 | 719 | returns `true` iff all attributes of the object are `false` 720 | 721 | #### Usage 722 | ```TypeScript 723 | import { arePropertiesFalse } from 'typesafe-utils' 724 | 725 | type Product = { 726 | count: number 727 | available: string 728 | } 729 | 730 | const items: Product[] = [ ... ] 731 | 732 | const result = items.filter(arePropertiesFalse('count', 'available')) 733 | ``` 734 | 735 | ### arePropertiesNotFalse 736 | 737 | returns `true` iff all attributes of the object are **not** `false` 738 | 739 | #### Usage 740 | ```TypeScript 741 | import { arePropertiesNotFalse } from 'typesafe-utils' 742 | 743 | type Product = { 744 | count: number 745 | available: string 746 | } 747 | 748 | const items: Product[] = [ ... ] 749 | 750 | const result = items.filter(arePropertiesNotFalse('count', 'available')) 751 | ``` 752 | 753 | 754 | 755 | ### isNumber 756 | 757 | returns `true` iff value is of type `number` 758 | 759 | #### Usage 760 | ```TypeScript 761 | import { isNumber } from 'typesafe-utils' 762 | 763 | const result = [0, false, undefined, 5].filter(isNumber) 764 | // result: number[] => [0, 5] 765 | ``` 766 | 767 | ### isZero 768 | 769 | returns `true` iff value is `0` 770 | 771 | #### Usage 772 | ```TypeScript 773 | import { isZero } from 'typesafe-utils' 774 | 775 | const result = [0, false, undefined, 5].filter(isZero) 776 | // result: 0[] => [0] 777 | ``` 778 | 779 | ### isNotZero 780 | 781 | returns `true` iff value is **not** `0` 782 | 783 | > Note: it is currently **not** possible to make this function fully typesafe.\ 784 | > `[0, null].filter(isNotTrue)` will have the type `(number | null)[]` 785 | 786 | #### Usage 787 | ```TypeScript 788 | import { isNotZero } from 'typesafe-utils' 789 | 790 | const result = [0, 123].filter(isNotZero) 791 | // result: number[] => [123] 792 | ``` 793 | 794 | ### isPropertyZero 795 | 796 | returns `true` iff the attribute of the object is `0` 797 | 798 | #### Usage 799 | ```TypeScript 800 | import { isPropertyZero } from 'typesafe-utils' 801 | 802 | type Product = { 803 | price: number 804 | } 805 | 806 | const items: Product[] = [ 807 | { price: 0 }, 808 | { price: 4 }, 809 | { price: 15 } 810 | ] 811 | const result = items.filter(isPropertyZero('price')) 812 | // result: Product[] => [{ price: 0 }] 813 | ``` 814 | 815 | ### isPropertyNotZero 816 | 817 | returns `true` iff the attribute of the object is **not** `0` 818 | 819 | #### Usage 820 | ```TypeScript 821 | import { isPropertyNotZero } from 'typesafe-utils' 822 | 823 | type Product = { 824 | price: number 825 | } 826 | 827 | const items: Product[] = [ 828 | { price: 0 }, 829 | { price: 12 } 830 | ] 831 | const result = items.filter(isPropertyNotZero('price')) 832 | // result: Product[] => [{ price: 23 }] 833 | ``` 834 | 835 | ### arePropertiesZero 836 | 837 | returns `true` iff all attributes of the object are `0` 838 | 839 | #### Usage 840 | ```TypeScript 841 | import { arePropertiesZero } from 'typesafe-utils' 842 | 843 | type Product = { 844 | count: number 845 | speed: string 846 | } 847 | 848 | const items: Product[] = [ ... ] 849 | 850 | const result = items.filter(arePropertiesZero('count', 'speed')) 851 | ``` 852 | 853 | ### arePropertiesNotZero 854 | 855 | returns `true` iff all attributes of the object are **not** `0` 856 | 857 | #### Usage 858 | ```TypeScript 859 | import { arePropertiesNotZero } from 'typesafe-utils' 860 | 861 | type Product = { 862 | count: number 863 | speed: string 864 | } 865 | 866 | const items: Product[] = [ ... ] 867 | 868 | const result = items.filter(arePropertiesNotZero('count', 'speed')) 869 | ``` 870 | 871 | 872 | 873 | ### isString 874 | 875 | returns `true` iff value is of type `string` 876 | 877 | #### Usage 878 | ```TypeScript 879 | import { isString } from 'typesafe-utils' 880 | 881 | const result = ['', false, null, 'text'].filter(isString) 882 | // result: string[] => ['', 'text] 883 | ``` 884 | 885 | ### isEmpty 886 | 887 | returns `true` iff value is `''` 888 | 889 | #### Usage 890 | ```TypeScript 891 | import { isEmpty } from 'typesafe-utils' 892 | 893 | const result = ['', false, null, 'text'].filter(isEmpty) 894 | // result: ''[] => [''] 895 | ``` 896 | 897 | ### isNotEmpty 898 | 899 | returns `true` iff value is **not** `''` 900 | 901 | #### Usage 902 | ```TypeScript 903 | import { isNotEmpty } from 'typesafe-utils' 904 | 905 | const result = ['', 5].filter(isNotEmpty) 906 | // result: number[] => [5] 907 | ``` 908 | 909 | ### isPropertyEmpty 910 | 911 | returns `true` iff the attribute of the object is `''` 912 | 913 | #### Usage 914 | ```TypeScript 915 | import { isPropertyEmpty } from 'typesafe-utils' 916 | 917 | type Product = { 918 | label: string 919 | } 920 | 921 | const items: Product[] = [ 922 | { label: '' }, 923 | { label: 'label-1' } 924 | ] 925 | const result = items.filter(isPropertyEmpty('label')) 926 | // result: Product[] => [{ label: '' }] 927 | ``` 928 | 929 | ### isPropertyNotEmpty 930 | 931 | returns `true` iff the attribute of the object is **not** `''` 932 | 933 | #### Usage 934 | ```TypeScript 935 | import { isPropertyNotEmpty } from 'typesafe-utils' 936 | 937 | type Product = { 938 | label: string 939 | } 940 | 941 | const items: Product[] = [ 942 | { label: 'label-123' }, 943 | { label: '' } 944 | ] 945 | const result = items.filter(isPropertyNotEmpty('label')) 946 | // result: Product[] => [{ label: 'label-123' }] 947 | ``` 948 | 949 | 950 | ### arePropertiesEmpty 951 | 952 | returns `true` iff all attributes of the object are `''` 953 | 954 | #### Usage 955 | ```TypeScript 956 | import { arePropertiesEmpty } from 'typesafe-utils' 957 | 958 | type Person = { 959 | name: number 960 | firstName: string 961 | } 962 | 963 | const items: Person[] = [ ... ] 964 | 965 | const result = items.filter(arePropertiesEmpty('name', 'firstName')) 966 | ``` 967 | 968 | ### arePropertiesNotEmpty 969 | 970 | returns `true` iff all attributes of the object are **not** `''` 971 | 972 | #### Usage 973 | ```TypeScript 974 | import { arePropertiesNotEmpty } from 'typesafe-utils' 975 | 976 | type Person = { 977 | name: number 978 | firstName: string 979 | } 980 | 981 | const items: Person[] = [ ... ] 982 | 983 | const result = items.filter(arePropertiesNotEmpty('name', 'firstName')) 984 | ``` 985 | 986 | 987 | 988 | ### isArray 989 | 990 | returns `true` iff value is of type `Array` 991 | 992 | #### Usage 993 | ```TypeScript 994 | import { isArray } from 'typesafe-utils' 995 | 996 | const result = [[], null, 123, [0, 1]].filter(isArray) 997 | // result: number[][] => [[], [0, 1]] 998 | ``` 999 | 1000 | ### isArrayNotEmpty 1001 | 1002 | returns `true` iff an array contains at least one item 1003 | 1004 | #### Usage 1005 | ```TypeScript 1006 | import { isArrayNotEmpty } from 'typesafe-utils' 1007 | 1008 | const nonEmptyArray = ['hi'] 1009 | 1010 | if (!!nonEmptyArray.length) { 1011 | nonEmptyArray[0].toUpperCase() // ERROR: Object is possibly 'undefined' 1012 | } 1013 | 1014 | if (isArrayNotEmpty(nonEmptyArray)) { 1015 | // TypeScript will know that the array contains at least 1 item, so it will not complain 1016 | nonEmptyArray[0].toUpperCase() 1017 | } 1018 | ``` 1019 | 1020 | ### isArrayEmpty 1021 | 1022 | returns `true` iff an array contains no items 1023 | 1024 | #### Usage 1025 | ```TypeScript 1026 | import { isArrayEmpty } from 'typesafe-utils' 1027 | 1028 | const emptyArray: string[] = [] 1029 | if (isArrayEmpty(emptyArray)) { 1030 | // emptyArray does not contain any items 1031 | } 1032 | ``` 1033 | 1034 | 1035 | 1036 | ### isObject 1037 | 1038 | returns `true` iff value is of type `object` 1039 | 1040 | #### Usage 1041 | ```TypeScript 1042 | import { isObject } from 'typesafe-utils' 1043 | 1044 | type SomeType = { 1045 | prop?: number 1046 | } 1047 | 1048 | const now = new Date() 1049 | const result = [{}, now, null, { prop: 123 }].filter(isObject) 1050 | // result: (SomeType | Date)[] => [{}, now, { prop: 123 }] 1051 | ``` 1052 | 1053 | ### isPrimitiveObject 1054 | 1055 | returns `true` iff value is of the primitive type `object` and not derived from a `class` like `Date` or else. 1056 | 1057 | #### Usage 1058 | ```TypeScript 1059 | import { isPrimitiveObject } from 'typesafe-utils' 1060 | 1061 | type SomeType = { 1062 | prop?: number 1063 | } 1064 | 1065 | const now = new Date() 1066 | const result = [{}, now, null, { prop: 123 }].filter(isPrimitiveObject) 1067 | // result: SomeType[] => [{}, { prop: 123 }] 1068 | ``` 1069 | 1070 | 1071 | 1072 | 1073 | ### filterDuplicates 1074 | 1075 | Removes duplicates from an array. Only the first occurrence of an item will be kept. 1076 | 1077 | #### Usage 1078 | 1079 | ```TypeScript 1080 | import { filterDuplicates } from 'typesafe-utils' 1081 | 1082 | const items = [1, 2, 3, 5, 8, 1] 1083 | const filteredItems = items.filter(filterDuplicates) 1084 | // filteredItems: number[] => [1, 2, 3, 5, 8] 1085 | ``` 1086 | 1087 | ### filterDuplicatesByKey 1088 | 1089 | Removes duplicates from an array by its key. Only the first occurrence of an item will be kept. 1090 | 1091 | > 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. 1092 | 1093 | #### Usage 1094 | 1095 | ```TypeScript 1096 | import { filterDuplicatesByKey } from 'typesafe-utils' 1097 | 1098 | type Product = { 1099 | id: number 1100 | name: string 1101 | } 1102 | 1103 | const items: Product[] = [ 1104 | { id: 1, name: 'name-1' }, 1105 | { id: 2, name: 'name-2' }, 1106 | { id: 3, name: 'name-1' }, 1107 | { id: 4, name: 'name-2' } 1108 | ] 1109 | const filteredItems = items.filter(filterDuplicatesByKey('name')) 1110 | // filteredItems: Product[] => [{ id: 1, name: 'name-1' }, { id: 2, name: 'name-2' }] 1111 | 1112 | 1113 | const willThrowAnError = items.filter(filterDuplicatesByKey('price')) 1114 | // throws: Argument of type '"price"' is **not** assignable to parameter of type '"id" | "name"' 1115 | ``` 1116 | 1117 | ### and 1118 | 1119 | Combines (`&&`) multiple filter functions. 1120 | 1121 | #### Usage 1122 | 1123 | ```TypeScript 1124 | import { and, isString } from 'typesafe-utils' 1125 | 1126 | const items = [null, "test", undefined, "hi"] 1127 | const isShortString = and(isString, (value) => value.length < 3) 1128 | const filteredItems = items.filter(isShortString) 1129 | // filteredItems: string[] => ['hi'] 1130 | ``` 1131 | 1132 | ### or 1133 | 1134 | Combines (`||`) multiple filter functions. 1135 | 1136 | #### Usage 1137 | 1138 | ```TypeScript 1139 | import { or } from 'typesafe-utils' 1140 | 1141 | const items = [10, 2, 3, 5, 8, 1] 1142 | const isFiveOrTen = or((value) => value === 5, (value) => value === 10) 1143 | const filteredItems = items.filter(isFiveOrTen) 1144 | // filteredItems: number[] => [10, 5] 1145 | ``` 1146 | 1147 | 1148 | ### not 1149 | 1150 | Inverts a filter function. 1151 | 1152 | #### Usage 1153 | 1154 | ```TypeScript 1155 | import { not, filterDuplicates } from 'typesafe-utils' 1156 | 1157 | type Product = { 1158 | id: number 1159 | name: string 1160 | } 1161 | 1162 | const items: Product[] = [ 1163 | { id: 1, name: 'name-1' }, 1164 | { id: 2, name: 'name-2' }, 1165 | { id: 3, name: 'name-1' }, 1166 | { id: 4, name: 'name-2' } 1167 | ] 1168 | const filteredItems = items.filter(not(filterDuplicatesByKey('name'))) 1169 | // filteredItems: Product[] => [{ id: 3, name: 'name-1' }, { id: 4, name: 'name-2' }] 1170 | 1171 | 1172 | // The `not` function takes two optional type arguments. 1173 | // The first is the type you expect the filter function to return. 1174 | // The second is the Type of the Array you want to filter. 1175 | // e.g. 1176 | const notNull = [1, 5, null].filter(not((value => value === null))) 1177 | // notNull: number[] => [1, 5] 1178 | 1179 | ``` 1180 | 1181 | 1182 | 1183 | ### createFilter 1184 | 1185 | Creates a typeguard filter. 1186 | 1187 | #### Usage 1188 | 1189 | ```TypeScript 1190 | import { createFilter } from 'typesafe-utils' 1191 | 1192 | interface Item { 1193 | id: number 1194 | } 1195 | 1196 | interface ItemWithName extends Item { 1197 | name: string 1198 | } 1199 | 1200 | const items: (Item | ItemWithName | undefined)[] = [ 1201 | { id: 1 }, 1202 | undefined 1203 | { id: 3, name: 'name-1' }, 1204 | { id: 4 } 1205 | ] 1206 | 1207 | const filterHasName = createFilter((item) => !!item?.name) 1208 | const filteredItems = items.filter(filterHasName) 1209 | // filteredItems: ItemWithName[] => [{ id: 3, name: 'name-1' }] 1210 | ``` 1211 | 1212 | 1213 | 1214 | 1215 | ## sorting functions 1216 | 1217 | ### sortNumberASC 1218 | 1219 | sort `number` in **ASC** order 1220 | 1221 | #### Usage 1222 | ```TypeScript 1223 | import { sortNumberASC } from 'typesafe-utils' 1224 | 1225 | const items = [4, -1, 3, 0] 1226 | const result = items.sort(sortNumberASC) 1227 | // result: number[] => [-1, 0, 3, 4] 1228 | ``` 1229 | 1230 | ### sortNumberDESC 1231 | 1232 | sort `number` in **DESC** order 1233 | 1234 | #### Usage 1235 | ```TypeScript 1236 | import { sortNumberDESC } from 'typesafe-utils' 1237 | 1238 | const items = [2, -5, 0] 1239 | const result = items.sort(sortNumberDESC) 1240 | // result: number[] => [2, 0, -5] 1241 | ``` 1242 | 1243 | ### sortNumberPropertyASC 1244 | 1245 | sort property of type `number` in **ASC** order 1246 | 1247 | #### Usage 1248 | ```TypeScript 1249 | import { sortNumberPropertyASC } from 'typesafe-utils' 1250 | 1251 | type Car { 1252 | speed: number 1253 | } 1254 | 1255 | const items: Car[] = [ 1256 | { speed: 113 }, 1257 | { speed: 100 }, 1258 | { speed: 95 } 1259 | ] 1260 | const result = items.sort(sortNumberPropertyASC('speed')) 1261 | // result: Car[] => [{ speed: 95 }, { speed: 100 }, { speed: 113 }} 1262 | ``` 1263 | 1264 | ### sortNumberPropertyDESC 1265 | 1266 | sort property of type `number` in **DESC** order 1267 | 1268 | #### Usage 1269 | ```TypeScript 1270 | import { sortNumberPropertyDESC } from 'typesafe-utils' 1271 | 1272 | type Car { 1273 | speed: number 1274 | } 1275 | 1276 | const items: Car[] = [ 1277 | { speed: 70 } 1278 | { speed: 87 } 1279 | ] 1280 | const result = items.sort(sortNumberPropertyDESC('speed')) 1281 | // result: Car[] => [{ speed: 87 }, { speed: 70 }] 1282 | ``` 1283 | 1284 | ### sortStringASC 1285 | 1286 | sort `string` in **ASC** order 1287 | 1288 | #### Usage 1289 | ```TypeScript 1290 | import { sortStringASC } from 'typesafe-utils' 1291 | 1292 | const items = ['Hi', 'apples'] 1293 | const result = items.sort(sortStringASC) 1294 | // result: string[] => ['apples', Hi'] 1295 | ``` 1296 | 1297 | ### sortStringDESC 1298 | 1299 | sort `string` in **DESC** order 1300 | 1301 | #### Usage 1302 | ```TypeScript 1303 | import { sortStringDESC } from 'typesafe-utils' 1304 | 1305 | const items = ['apple', 'banana'] 1306 | const result = items.sort(sortStringDESC) 1307 | // result: string[] => ['banana', 'apple'] 1308 | ``` 1309 | 1310 | ### sortStringPropertyASC 1311 | 1312 | sort property of type `string` in **ASC** order 1313 | 1314 | #### Usage 1315 | ```TypeScript 1316 | import { sortStringPropertyASC } from 'typesafe-utils' 1317 | 1318 | type Car { 1319 | color: string 1320 | } 1321 | 1322 | const items: Car[] = [ 1323 | { color: 'green' }, 1324 | { color: 'brown' } 1325 | ] 1326 | 1327 | const result = items.sort(sortStringPropertyASC('color')) 1328 | // result: Car[] => [{ color: 'brown' }, { color: 'green' }] 1329 | ``` 1330 | 1331 | ### sortStringPropertyDESC 1332 | 1333 | sort property of type `string` in **DESC** order 1334 | 1335 | #### Usage 1336 | ```TypeScript 1337 | import { sortStringPropertyDESC } from 'typesafe-utils' 1338 | 1339 | type Car { 1340 | color: string 1341 | } 1342 | 1343 | const items: Car[] = [ 1344 | { color: 'red' }, 1345 | { color: 'blue' } 1346 | ] 1347 | const result = items.sort(sortStringPropertyDESC('color')) 1348 | // result: Car[] => [{ color: 'red' }, { color: 'blue' }] 1349 | ``` 1350 | 1351 | ### sortDateASC 1352 | 1353 | sort `Date` in **ASC** order 1354 | 1355 | #### Usage 1356 | ```TypeScript 1357 | import { sortDateASC } from 'typesafe-utils' 1358 | 1359 | const today = new Date() 1360 | const tomorrow = new Date(today.getTime() + 24 * 60 * 60 * 1000) 1361 | 1362 | const items = [tomorrow, today] 1363 | const result = items.sort(sortDateASC) 1364 | // result: Date[] => [today, tomorrow] 1365 | ``` 1366 | 1367 | ### sortDateDESC 1368 | 1369 | sort `Date` in **DESC** order 1370 | 1371 | #### Usage 1372 | ```TypeScript 1373 | import { sortDateDESC } from 'typesafe-utils' 1374 | 1375 | const today = new Date() 1376 | const tomorrow = new Date(today.getTime() + 24 * 60 * 60 * 1000) 1377 | 1378 | const items = [today, tomorrow] 1379 | const result = items.sort(sortDateDESC) 1380 | // result: Date[] => [tomorrow, today] 1381 | ``` 1382 | 1383 | ### sortDatePropertyASC 1384 | 1385 | sort property of type `Date` in **ASC** order 1386 | 1387 | #### Usage 1388 | ```TypeScript 1389 | import { sortDatePropertyASC } from 'typesafe-utils' 1390 | 1391 | type Smartphone = { 1392 | releaseDate: Date 1393 | } 1394 | 1395 | const today = new Date() 1396 | const tomorrow = new Date(today.getTime() + 24 * 60 * 60 * 1000) 1397 | 1398 | const items: Smartphone[] = [ 1399 | { releaseDate: tomorrow }, 1400 | { releaseDate: today } 1401 | ] 1402 | 1403 | const items: Smartphone[] = [] 1404 | const result = items.sort(sortDatePropertyASC('releaseDate')) 1405 | // result: Smartphone[]=> [{ releaseDate: today }, { releaseDate: tomorrow }] 1406 | ``` 1407 | 1408 | ### sortDatePropertyDESC 1409 | 1410 | sort property of type `Date` in **DESC** order 1411 | 1412 | #### Usage 1413 | ```TypeScript 1414 | import { sortDatePropertyDESC } from 'typesafe-utils' 1415 | 1416 | type Smartphone = { 1417 | releaseDate: Date 1418 | } 1419 | 1420 | const today = new Date() 1421 | const tomorrow = new Date(today.getTime() + 24 * 60 * 60 * 1000) 1422 | 1423 | const items: Smartphone[] = [ 1424 | { releaseDate: today }, 1425 | { releaseDate: tomorrow } 1426 | ] 1427 | const result = items.sort(sortDatePropertyDESC('releaseDate')) 1428 | // result: Smartphone[] => [{ releaseDate: tomorrow }, { releaseDate: today }] 1429 | ``` 1430 | 1431 | 1432 | 1433 | 1434 | ## mapping functions 1435 | 1436 | ### pick 1437 | 1438 | Picks an attribute from an Object. 1439 | 1440 | #### Usage 1441 | 1442 | ```TypeScript 1443 | import { pick } from 'typesafe-utils' 1444 | 1445 | interface Item { 1446 | id: number 1447 | name: string 1448 | price: number 1449 | } 1450 | 1451 | const items: Item[] = [ 1452 | { id: 1, name: '', price: 123 }, 1453 | { id: 3, name: '', price: 0 }, 1454 | { id: 7, name: '', price: 12 }, 1455 | ] 1456 | 1457 | const ids = items.map(pick('id')) 1458 | // ids: number[] => [ 1, 3, 7 ] 1459 | ``` 1460 | 1461 | 1462 | 1463 | 1464 | ## other 1465 | 1466 | ### deepClone 1467 | 1468 | Creates a deep copy of an object containing primitive values. 1469 | 1470 | > 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. 1471 | 1472 | #### Usage 1473 | 1474 | ```TypeScript 1475 | import { deepClone } from 'typesafe-utils' 1476 | 1477 | const objectToClone: MyTypedObject = { ... } 1478 | const clonedObject = deepClone(objectToClone) 1479 | // => clonedObject: MyTypedObject => { ... } 1480 | ``` 1481 | 1482 | 1483 | ### uniqueArray 1484 | 1485 | Removes duplicates from an array. 1486 | 1487 | #### Usage 1488 | 1489 | ```TypeScript 1490 | import { uniqueArray } from 'typesafe-utils' 1491 | 1492 | const unique = uniqueArray('John', 'Max', 'John') 1493 | // => unique: string[] => ['John', 'Max'] 1494 | ``` 1495 | 1496 | 1497 | 1498 | 1499 | ## Types 1500 | 1501 | ### Truthy 1502 | 1503 | Contains all `Truthy` values (everything excluding [Falsy](#falsy) values) 1504 | 1505 | #### Usage 1506 | 1507 | ```TypeScript 1508 | import { Truthy } from 'typesafe-utils' 1509 | 1510 | export const isTruthy = (value: T): value is Truthy => !!value 1511 | 1512 | const truthy = [123, undefined].filter(isTruthy) // => number[] 1513 | const notTruthy = [false, true].filter(isTruthy) // => never[] 1514 | ``` 1515 | 1516 | 1517 | ### Falsy 1518 | 1519 | Contains all `Falsy` values (false | '' | 0 | null | undefined) 1520 | 1521 | #### Usage 1522 | 1523 | ```TypeScript 1524 | import { Falsy } from 'typesafe-utils' 1525 | 1526 | export const isFalsy = (value: T): value is Falsy => !value 1527 | 1528 | const falsy = [undefined, ''].filter(isFalsy) // => undefined[] 1529 | const notFalsy = [0, ''].filter(isFalsy) // => never[] 1530 | ``` 1531 | 1532 | 1533 | ### TypeGuard 1534 | ### TypeGuardWithReturnType 1535 | 1536 | Allows you to write custom `TypeGuard` functions. 1537 | 1538 | #### Usage 1539 | 1540 | ```TypeScript 1541 | import { TypeGuard } from 'typesafe-utils' 1542 | 1543 | type Project { 1544 | id: number 1545 | // ... 1546 | } 1547 | 1548 | const isProject = (value: T): value is TypeGuard => value?.hasOwnProperty('id') 1549 | 1550 | const p1 = isProject({ id: 1 }) // => true 1551 | const p2 = isProject(true) // => false 1552 | ``` 1553 | 1554 | 1555 | ### TypeGuardInverted 1556 | ### TypeGuardInvertedWithReturnType 1557 | 1558 | Allows you to write custom inverted `TypeGuard` functions. 1559 | 1560 | #### Usage 1561 | 1562 | ```TypeScript 1563 | import { TypeGuardInverted } from 'typesafe-utils' 1564 | 1565 | type Project { 1566 | id: number 1567 | // ... 1568 | } 1569 | 1570 | const isNotProject = (value: T): value is TypeGuardInverted => !value?.hasOwnProperty('id') 1571 | 1572 | const p1 = isNotProject({ id: 1 }) // => false 1573 | const p2 = isNotProject(null) // => true 1574 | ``` 1575 | --------------------------------------------------------------------------------