@ppwcode/js-ts-oddsandends
v2.0.1
Published
TypeScript utility library: assertion functions and Joi schema test helpers
Keywords
Readme
@ppwcode/js-ts-oddsandends
Small TypeScript utility library: assertion predicates, conditional-assert guards, and Joi schema test helpers.
Requirements
- Node.js ≥ 24.16.0
- Pure ESM — use
import, notrequire.
Installation
npm install @ppwcode/js-ts-oddsandendsAPI
The assertion and conditional-assertion symbols are exported from the package root:
import {
assert,
ConditionViolation,
containsNoDuplicates,
isNatural,
notNull,
notUndefined,
settings
} from '@ppwcode/js-ts-oddsandends'expectSeriousSchema is available via a dedicated subpath import:
import { expectSeriousSchema } from '@ppwcode/js-ts-oddsandends/expectSeriousSchema'Assertion predicates
Simple boolean predicates. Useful as arguments to assert or Joi .custom().
isNatural(i?: number): boolean
Returns true when i is undefined or a non-negative integer.
isNatural() // true
isNatural(0) // true
isNatural(7) // true
isNatural(-1) // false
isNatural(Math.PI) // falsecontainsNoDuplicates<T>(arr: Array<T>): boolean
Returns true when the array contains no duplicate entries (compared with ===).
containsNoDuplicates([1, 2, 3]) // true
containsNoDuplicates([1, 2, 1]) // falseConditional assertions
Runtime guards that throw ConditionViolation when an assertion fails. Guards are a no-op when
settings.enabled is false (useful for disabling assertions in production if you choose to).
settings
settings.enabled // default: true — all guards are active
settings.logViolations // default: false — log to console.error before throwingassert<T, U extends T>(subject: T, assertion: (t: T) => t is U, message?: string): asserts subject is U
assert<T>(subject: T, assertion: (t: T) => boolean, message?: string): void
Throws ConditionViolation if assertion(subject) is falsy.
When assertion is a TypeScript type guard, TypeScript narrows the type of subject after the call. When
assertion is a plain boolean predicate, the return is void and no narrowing occurs.
assert(count, Number.isInteger)
assert(index, i => i >= 0, 'index must be non-negative')
// with a type guard — subject is narrowed to string after this line:
assert(value, (v): v is string => typeof v === 'string')notUndefined<T>(t?: T): asserts t is T
Asserts that t is not undefined. Narrows the type of t in-place.
const value: string | undefined = map.get(key)
notUndefined(value)
const definite: string = value // TypeScript knows value is string herenotNull<T>(t: T | null): asserts t is T
Asserts that t is not null. Narrows the type of t in-place.
const node: Element | null = document.getElementById('root')
notNull(node)
node.classList.add('active') // TypeScript knows node is Element hereConditionViolation
Error subclass thrown when an assertion fails.
Schema test helper
Test helper for Joi schema validation. Works with mocha, jasmine, vitest (with globals: true), and
node:test — the active framework is detected automatically.
Peer dependency:
joimust be in your owndependenciesordevDependencies. It is not bundled.
expectSeriousSchema(schema, failures, unknownNotAllowed?, context?, testApi?): void
Registers a suite of it() calls (nested inside a describe) that verify a Joi schema is well-formed:
- is a recognised Joi schema
- has a description
- has at least one example value that validates
- rejects every value in
failures - optionally rejects unknown keys
import { expectSeriousSchema } from '@ppwcode/js-ts-oddsandends/expectSeriousSchema'
import { mySchema } from '../src/my-schema.js'
describe('mySchema', () => {
expectSeriousSchema(mySchema, [null, undefined, 42, 'bad'])
})Framework compatibility
expectSeriousSchema detects the active test framework at call time by checking globalThis.describe and
globalThis.it. Supported configurations:
| Framework | Works automatically? |
| --------- | -------------------- |
| mocha | ✓ — injects describe/it as globals |
| jasmine | ✓ — injects describe/it as globals |
| vitest with globals: true | ✓ — injects describe/it as globals |
| node:test | ✓ — used as fallback when no global describe/it is found |
| vitest with globals: false (default) | ✗ — pass testApi explicitly (see below) |
testApi escape hatch
When vitest runs with its default config (globals: false), describe and it are not injected as globals and
auto-detection falls back to node:test. Pass them explicitly as the last argument:
import { describe, it } from 'vitest'
import { expectSeriousSchema } from '@ppwcode/js-ts-oddsandends/expectSeriousSchema'
import { mySchema } from '../src/my-schema.js'
describe('mySchema', () => {
expectSeriousSchema(mySchema, [null, undefined, 42, 'bad'], false, undefined, { describe, it })
})Migrating from v1
See CHANGES.md for a full list of breaking changes.
Key actions:
- Switch from
require()toimport. - Add
joito your owndependenciesordevDependenciesif you useexpectSeriousSchema. - Import assertion and conditional-assertion symbols from
@ppwcode/js-ts-oddsandends; importexpectSeriousSchemafrom@ppwcode/js-ts-oddsandends/expectSeriousSchema.
License
MIT — Copyright 2023–2026 PeopleWare n.v. See LICENSE.
