Utilities
Collection of utility functions for type checking, object manipulation, and common transformations. All exports are tree-shakeable.
Import
import {
// Type guards
isFunction, isString, isNumber, isBoolean,
isObject, isArray, isElement,
isNull, isUndefined, isNullOrUndefined,
isPrimitive, isSymbol, isNaN,
// Object manipulation
mergeDeep,
// Utilities
useId, clamp, range, debounce
} from '#v0/utilities'
Type Guards
Type guard functions that perform runtime type checking with TypeScript type narrowing.
isFunction
Checks if a value is a function.
function isFunction(item: unknown): item is Function
True if the value is a function
Usage
isFunction(() => {}) // true
isFunction('string') // false
const value: unknown = someValue
if (isFunction(value)) {
value() // TypeScript knows value is a function
}
isString
Checks if a value is a string.
function isString(item: unknown): item is string
True if the value is a string
Usage
isString('hello') // true
isString(123) // false
const value: unknown = someValue
if (isString(value)) {
console.log(value.toUpperCase()) // TypeScript knows value is a string
}
isNumber
Checks if a value is a number.
function isNumber(item: unknown): item is number
True if the value is a number (including NaN)
This returns true for NaN. Use isNaN() to check for NaN specifically.
Usage
isNumber(123) // true
isNumber(NaN) // true
isNumber('123') // false
isBoolean
Checks if a value is a boolean.
function isBoolean(item: unknown): item is boolean
True if the value is a boolean
Usage
isBoolean(true) // true
isBoolean(false) // true
isBoolean(0) // false
isBoolean(1) // false
isObject
Checks if a value is a plain object (excludes null and arrays).
function isObject(item: unknown): item is Record<string, unknown>
True if the value is a plain object
Returns false for null and arrays, even though typeof null === 'object' and typeof [] === 'object' in JavaScript.
Usage
isObject({}) // true
isObject({ a: 1 }) // true
isObject(null) // false
isObject([]) // false
const value: unknown = someValue
if (isObject(value)) {
console.log(value.someProperty) // TypeScript knows value is an object
}
isArray
Checks if a value is an array.
function isArray(item: unknown): item is unknown[]
True if the value is an array
Usage
isArray([]) // true
isArray([1, 2, 3]) // true
isArray('string') // false
isArray({}) // false
isElement
Checks if a value is a DOM Element.
function isElement(item: unknown): item is Element
True if the value is a DOM Element
Usage
isElement(document.body) // true
isElement(document.querySelector('div')) // true (if found)
isElement('string') // false
isElement(null) // false
isNull
Checks if a value is null.
function isNull(item: unknown): item is null
True if the value is null
Usage
isNull(null) // true
isNull(undefined) // false
isNull(0) // false
isNull('') // false
isUndefined
Checks if a value is undefined.
function isUndefined(item: unknown): item is undefined
True if the value is undefined
Usage
isUndefined(undefined) // true
isUndefined(null) // false
isUndefined(0) // false
isNullOrUndefined
Checks if a value is null or undefined.
function isNullOrUndefined(item: unknown): item is null | undefined
True if the value is null or undefined
Uses loose equality (== null) which matches both null and undefined.
Usage
isNullOrUndefined(null) // true
isNullOrUndefined(undefined) // true
isNullOrUndefined(0) // false
isNullOrUndefined('') // false
// Useful for optional values
function process(value?: string) {
if (!isNullOrUndefined(value)) {
console.log(value.toUpperCase())
}
}
isPrimitive
Checks if a value is a primitive (string, number, or boolean).
function isPrimitive(item: unknown): item is string | number | boolean
True if the value is a string, number, or boolean
Usage
isPrimitive('hello') // true
isPrimitive(123) // true
isPrimitive(true) // true
isPrimitive({}) // false
isPrimitive(null) // false
isPrimitive(undefined) // false
isSymbol
Checks if a value is a symbol.
function isSymbol(item: unknown): item is symbol
True if the value is a symbol
Usage
isSymbol(Symbol('test')) // true
isSymbol('symbol') // false
isNaN
Checks if a value is NaN (Not a Number).
function isNaN(item: unknown): item is number
Uses Number.isNaN() which only returns true for the actual NaN value, unlike the global isNaN() which coerces the argument to a number first.
Usage
isNaN(NaN) // true
isNaN(123) // false
isNaN('hello') // false (unlike global isNaN)
isNaN(undefined) // false (unlike global isNaN)
Object Manipulation
mergeDeep
Deeply merges source objects into a target object.
function mergeDeep<T extends object>(
target: T,
...sources: DeepPartial<T>[]
): T
The target object to merge into (will be mutated)
One or more source objects to merge from
The mutated target object
- Mutates the target object in place
- Arrays are replaced, not merged
- Protected against prototype pollution (
__proto__, constructor, prototype)
Usage
// Basic merge
const target = { a: 1, b: { c: 2 } }
mergeDeep(target, { b: { d: 3 } })
// target is now { a: 1, b: { c: 2, d: 3 } }
// Multiple sources
const result = mergeDeep({}, { a: 1 }, { b: 2 }, { c: 3 })
// result is { a: 1, b: 2, c: 3 }
// Arrays are replaced
mergeDeep({ arr: [1, 2] }, { arr: [3] })
// { arr: [3] }
// Deep nested objects
const config = {
theme: {
colors: { primary: 'blue' },
spacing: { base: 16 }
}
}
mergeDeep(config, {
theme: {
colors: { secondary: 'red' }
}
})
// {
// theme: {
// colors: { primary: 'blue', secondary: 'red' },
// spacing: { base: 16 }
// }
// }
Utilities
useId
Generates a unique ID, using Vue’s useId when in component context.
- In component setup/lifecycle: Uses Vue’s
useId() for SSR-safe hydration
- Outside components: Falls back to sequential counter (
v0-0, v0-1, …)
- Vapor mode compatible
Usage
// In component setup - SSR safe
const id = useId() // 'v:0', 'v:1', etc. (Vue's format)
// Outside component - counter fallback
const id = useId() // 'v0-0', 'v0-1', etc.
<script setup lang="ts">
import { useId } from '#v0/utilities'
const inputId = useId()
const labelId = useId()
</script>
<template>
<div>
<label :for="inputId" :id="labelId">
Username
</label>
<input :id="inputId" :aria-labelledby="labelId" />
</div>
</template>
clamp
Clamps a value between a minimum and maximum.
function clamp(value: number, min = 0, max = 1): number
Usage
clamp(5, 0, 10) // 5
clamp(-5, 0, 10) // 0
clamp(15, 0, 10) // 10
clamp(0.5) // 0.5 (default range 0-1)
clamp(1.5) // 1 (default max is 1)
// Useful for percentages
const percentage = clamp(progress / total, 0, 1)
// Useful for ranges
const volume = clamp(newVolume, 0, 100)
range
Creates an array of sequential numbers.
function range(length: number, start = 0): number[]
The length of the array to create
An array of sequential numbers
Usage
range(3) // [0, 1, 2]
range(3, 1) // [1, 2, 3]
range(5, 10) // [10, 11, 12, 13, 14]
range(0) // []
// Useful for rendering
const items = range(10).map(i => ({ id: i, name: `Item ${i}` }))
// Useful for pagination
const pages = range(totalPages, 1) // [1, 2, 3, ...]
debounce
Debounces a function call by the specified delay.
function debounce<T extends (...args: any[]) => any>(
fn: T,
delay: number
): {
(...args: Parameters<T>): void
clear(): void
immediate(...args: Parameters<T>): void
}
The delay in milliseconds
A debounced function with clear() and immediate() methods
Usage
const debouncedFn = debounce(() => console.log('called'), 500)
// Will call after 500ms of inactivity
debouncedFn()
// Cancel pending call
debouncedFn.clear()
// Call immediately
debouncedFn.immediate()
<script setup lang="ts">
import { debounce } from '#v0/utilities'
const search = ref('')
const performSearch = debounce((query: string) => {
console.log('Searching for:', query)
// Perform API call
}, 300)
watch(search, (value) => {
performSearch(value)
})
onUnmounted(() => {
performSearch.clear()
})
</script>
<template>
<input v-model="search" placeholder="Search..." />
</template>