Package Exports
- @avstantso/js
- @avstantso/js/dist/index.js
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (@avstantso/js) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
@avstantso/js
JavaScript runtime type checking and manipulation utilities for robust type-safe JavaScript operations.
Features
- Type checking utilities - Type-safe wrappers for
typeofandinstanceofwith convenient shortcuts - Switch by type - Pattern matching based on runtime type with support for
nulland default cases - Runtime type information - Attach and query type metadata to objects and functions
- Set membership testing - Check if values belong to specific sets with type safety
- Structure detection - Identify objects that can be used with
Object.defineProperty - Zero dependencies beyond the monorepo packages
Installation
npm install @avstantso/jsor
yarn add @avstantso/jsUsage
Global Access
Import the package to register the AVStantso.JS namespace globally:
import '@avstantso/js';
// Use utilities from the global namespace
if (AVStantso.JS.is.string(value)) {
console.log(value.toUpperCase());
}Direct Member Imports
Import the JS namespace directly for cleaner code:
import { JS } from '@avstantso/js';
// Use imported JS namespace
if (JS.is.string(value)) {
console.log(value.toUpperCase());
}
// Pattern matching on types
const result = JS.switch(data, {
string: (s) => s.length,
number: (n) => n * 2,
default: () => 0
});Exported Members:
JS- Complete JavaScript utilities namespace (same asAVStantso.JS)
API Reference
JS.Type
Union type of all JavaScript types (possible results of typeof operator).
Type Definition:
type Type = 'string' | 'number' | 'bigint' | 'boolean' | 'symbol' | 'undefined' | 'object' | 'function'JS.Types
Readonly map of JavaScript type names to themselves. Useful for type-safe comparisons.
Example:
import { JS } from '@avstantso/js';
console.log(JS.Types.string); // 'string'
console.log(JS.Types.number); // 'number'
// Use in comparisons
const type = typeof value;
if (type === JS.Types.string) {
// Type-safe string handling
}JS.is
Type-safe wrapper for typeof and instanceof checks with convenient shortcuts.
JS.is(type, candidate)
Check if candidate is of the specified type.
Parameters:
type- JavaScript type to check for ('string','number', etc.)candidate- Value to test
Returns: Type predicate candidate is T
Example:
import { JS } from '@avstantso/js';
const value: unknown = 'hello';
if (JS.is('string', value)) {
// TypeScript knows value is string here
console.log(value.toUpperCase());
}
if (JS.is('number', value)) {
// TypeScript knows value is number here
console.log(value.toFixed(2));
}Type Shortcuts
Convenient shortcuts for common type checks. All shortcuts provide type narrowing.
JS.is.string(candidate)
Check if candidate is a string.
Example:
import { JS } from '@avstantso/js';
if (JS.is.string(value)) {
console.log(value.toUpperCase()); // TypeScript knows value is string
}JS.is.number(candidate)
Check if candidate is a number.
Example:
import { JS } from '@avstantso/js';
if (JS.is.number(value)) {
console.log(value.toFixed(2)); // TypeScript knows value is number
}JS.is.bigint(candidate)
Check if candidate is a bigint.
JS.is.boolean(candidate)
Check if candidate is a boolean.
JS.is.symbol(candidate)
Check if candidate is a symbol.
JS.is.undefined(candidate)
Check if candidate is undefined.
JS.is.object(candidate)
Check if candidate is an object (including null).
Note: Remember that typeof null === 'object' in JavaScript.
JS.is.function(candidate)
Check if candidate is a function.
Example:
import { JS } from '@avstantso/js';
if (JS.is.function(value)) {
const result = value(); // TypeScript knows value is a function
}JS.is.class(objClass, candidate)
Check if candidate is an instance of the specified class.
Parameters:
objClass- Class constructor to check againstcandidate- Value to test
Returns: Type predicate candidate is T
Example:
import { JS } from '@avstantso/js';
class User {
constructor(public name: string) {}
}
const value: unknown = new User('John');
if (JS.is.class(User, value)) {
// TypeScript knows value is User here
console.log(value.name);
}
// Works with built-in classes too
if (JS.is.class(Date, value)) {
console.log(value.getTime());
}
if (JS.is.class(Array, value)) {
console.log(value.length);
}JS.is.error(errClass, candidate)
Check if candidate is an instance of the specified Error class.
Parameters:
errClass- Error class constructor to check againstcandidate- Value to test
Returns: Type predicate candidate is T extends Error
Example:
import { JS } from '@avstantso/js';
class ValidationError extends Error {
constructor(public field: string, message: string) {
super(message);
}
}
try {
throw new ValidationError('email', 'Invalid email format');
} catch (err) {
if (JS.is.error(ValidationError, err)) {
// TypeScript knows err is ValidationError
console.log(`Field ${err.field}: ${err.message}`);
} else if (JS.is.error(Error, err)) {
// TypeScript knows err is Error
console.log(err.message);
}
}JS.is.structure(candidate)
Check if candidate is a structure type (object or function) that can be used with Object.defineProperty.
Returns: Type predicate candidate is object | Function
Example:
import { JS } from '@avstantso/js';
function attachMetadata(target: unknown, key: string, value: any) {
if (JS.is.structure(target)) {
// TypeScript knows target is object | Function
Object.defineProperty(target, key, { value, enumerable: false });
}
}
const obj = {};
attachMetadata(obj, 'version', '1.0.0');
const func = () => {};
attachMetadata(func, 'description', 'My function');
attachMetadata('string', 'prop', 'value'); // Does nothing - strings aren't structuresJS.is.oneOf
Check if a candidate is of a specific type AND is a member of a given set.
JS.is.oneOf(type, candidate, ...set)
Check if candidate is of the specified type and is included in the set.
Parameters:
type- JavaScript type to check forcandidate- Value to test...set- Values to check membership against (can be array, Set, or spread arguments)
Returns: Type predicate candidate is T
Example:
import { JS } from '@avstantso/js';
const value: unknown = 'admin';
// Check if string and in allowed set
if (JS.is.oneOf('string', value, 'admin', 'user', 'guest')) {
console.log(`Valid role: ${value}`);
}
// Using an array
const validRoles = ['admin', 'user', 'guest'];
if (JS.is.oneOf('string', value, validRoles)) {
console.log(`Valid role: ${value}`);
}
// Using a Set
const validNumbers = new Set([1, 2, 3, 5, 8, 13]);
if (JS.is.oneOf('number', value, validNumbers)) {
console.log(`Fibonacci number: ${value}`);
}Type Shortcuts for oneOf
All shortcuts work the same way, checking both type and set membership.
JS.is.oneOf.string(candidate, ...set)
Check if candidate is a string in the given set.
Example:
import { JS } from '@avstantso/js';
type Status = 'pending' | 'active' | 'completed';
function processStatus(status: unknown) {
if (JS.is.oneOf.string(status, 'pending', 'active', 'completed')) {
// TypeScript knows status is string and one of the valid values
console.log(`Processing ${status} status`);
}
}JS.is.oneOf.number(candidate, ...set)
Check if candidate is a number in the given set.
Example:
import { JS } from '@avstantso/js';
const validPorts = [80, 443, 8080, 3000];
if (JS.is.oneOf.number(port, validPorts)) {
console.log(`Valid port: ${port}`);
}JS.is.oneOf.bigint(candidate, ...set)
Check if candidate is a bigint in the given set.
JS.is.oneOf.boolean(candidate, ...set)
Check if candidate is a boolean in the given set.
JS.is.oneOf.symbol(candidate, ...set)
Check if candidate is a symbol in the given set.
JS.is.oneOf.undefined(candidate, ...set)
Check if candidate is undefined in the given set.
JS.is.oneOf.object(candidate, ...set)
Check if candidate is an object in the given set.
JS.is.oneOf.function(candidate, ...set)
Check if candidate is a function in the given set.
JS.is.oneOf.class(objClass, candidate, ...set)
Check if candidate is an instance of the specified class and is in the given set.
Example:
import { JS } from '@avstantso/js';
class Admin extends User {}
class Moderator extends User {}
const validUserClasses = [Admin, Moderator];
if (JS.is.oneOf.class(User, candidate, validUserClasses)) {
// candidate is either Admin or Moderator instance
}JS.is.oneOf.error(errClass, candidate, ...set)
Check if candidate is an instance of the specified Error class and is in the given set.
JS.is.oneOf.structure(candidate, ...set)
Check if candidate is a structure and is in the given set.
JS.switch
Pattern matching utility based on runtime type with support for functions and default cases.
JS.switch<R>(data, cases)
Switch on value's runtime type and execute the corresponding case.
Type Parameters:
R- Return type of all casesT- Type of data being switched on (inferred)Cases- Type of cases object (inferred)
Parameters:
data- Value to switch on (usestypeofto determine type)cases- Object mapping type names to handlers or values
Returns: Result of type R
Case Keys:
- JavaScript types:
'string','number','bigint','boolean','symbol','undefined','object','function' - Special cases:
'null'(checked beforetypeof),'default'(fallback when no match)
Case Values:
- Static value of type
R - Function that receives the typed data and returns
R
Basic Usage
Example:
import { JS } from '@avstantso/js';
const value: unknown = 42;
const result = JS.switch(value, {
string: 'It is a string',
number: 'It is a number',
boolean: 'It is a boolean',
default: 'Unknown type'
});
console.log(result); // 'It is a number'Function Handlers
Example:
import { JS } from '@avstantso/js';
const value: unknown = 'hello';
const result = JS.switch(value, {
string: (s) => s.toUpperCase(),
number: (n) => n * 2,
boolean: (b) => !b,
default: () => null
});
console.log(result); // 'HELLO'Null Handling
The null case is checked before typeof, allowing you to distinguish null from other objects.
Example:
import { JS } from '@avstantso/js';
const value: unknown = null;
const result = JS.switch(value, {
null: 'Explicitly null',
object: 'Some object',
default: 'Other type'
});
console.log(result); // 'Explicitly null'Complex Example
Example:
import { JS } from '@avstantso/js';
function formatValue(value: unknown): string {
return JS.switch(value, {
string: (s) => `"${s}"`,
number: (n) => n.toFixed(2),
boolean: (b) => b ? 'YES' : 'NO',
null: 'NULL',
undefined: 'UNDEFINED',
object: (o) => {
if (Array.isArray(o)) return `[Array: ${o.length} items]`;
if (o instanceof Date) return o.toISOString();
return '[Object]';
},
function: (f) => `[Function: ${f.name || 'anonymous'}]`,
default: () => '[Unknown]'
});
}
console.log(formatValue('hello')); // "hello"
console.log(formatValue(42.5)); // 42.50
console.log(formatValue(true)); // YES
console.log(formatValue(null)); // NULL
console.log(formatValue(undefined)); // UNDEFINED
console.log(formatValue([1, 2, 3])); // [Array: 3 items]
console.log(formatValue(new Date())); // 2024-01-09T12:00:00.000Z
console.log(formatValue(() => {})); // [Function: anonymous]Type Inference
The return type is inferred from the cases:
Example:
import { JS } from '@avstantso/js';
// Return type inferred as number
const num = JS.switch(value, {
string: 1,
number: 2,
default: 0
});
// Return type inferred as string | number
const mixed = JS.switch(value, {
string: 'text',
number: 42,
default: 0
});JS.TypeInfo
Runtime type information system for attaching metadata to objects and functions.
JS.TypeInfo
Type definition for runtime type information metadata.
Type Definition:
type TypeInfo = NodeJS.ReadOnlyDict<TS.Literal>Description:
Runtime type information map where keys are property names and values are type literal strings ('string', 'number', etc.).
This is useful for scenarios like:
- Dynamic component rendering
- Form field type inference
- API schema validation
- Automatic serialization/deserialization
Example:
import { JS } from '@avstantso/js';
const userTypeInfo: JS.TypeInfo = {
id: 'number',
name: 'string',
email: 'string',
active: 'boolean',
createdAt: 'Date'
};JS.TypeInfo.Provider
Interface for objects that provide runtime type information.
Type Definition:
interface Provider {
typeInfo: TypeInfo;
}Example:
import { JS } from '@avstantso/js';
class User implements JS.TypeInfo.Provider {
typeInfo = {
id: 'number',
name: 'string',
email: 'string',
active: 'boolean'
} as const;
constructor(
public id: number,
public name: string,
public email: string,
public active: boolean
) {}
}
const user = new User(1, 'John', 'john@example.com', true);
console.log(user.typeInfo); // { id: 'number', name: 'string', ... }JS.hasTypeInfo(candidate)
Check if a value has attached type information.
Parameters:
candidate- Value to check
Returns: Type predicate candidate is TypeInfo.Provider
Example:
import { JS } from '@avstantso/js';
class Component {
typeInfo = { label: 'string', value: 'number' };
}
const obj1 = new Component();
const obj2 = { data: 'test' };
if (JS.hasTypeInfo(obj1)) {
// TypeScript knows obj1 has typeInfo property
console.log(obj1.typeInfo); // { label: 'string', value: 'number' }
}
if (JS.hasTypeInfo(obj2)) {
// This won't execute
console.log(obj2.typeInfo);
}JS.tryTypeInfo(candidate)
Attempt to retrieve type information from a value.
Parameters:
candidate- Value to extract type info from
Returns: TypeInfo if available, false otherwise
Example:
import { JS } from '@avstantso/js';
function renderField(html: string) {
const [component, data] = /* parse html to determine component and attributes */;
const typeInfo = JS.tryTypeInfo(component);
if (typeInfo) {
// Use type information to render appropriately
for (const [field, type] of Object.entries(typeInfo)) {
const value = data[field];
if (type === 'boolean' && value === undefined) {
// Interpret key without value boolean as true
data[field] = true;
}
}
}
return [component, data];
}
// Example: React component with type info
const InputField = {
typeInfo: {
label: 'string',
required: 'boolean',
maxLength: 'number'
},
render: (props: any) => { /* ... */ }
};
const [Component, props] = renderField('<InputField label="Email" maxLength=50 required />');
// props result: { label: 'Email', maxLength: 50, required: true }
Component.render(props);Use Case: Dynamic Form Rendering
Example:
import { JS } from '@avstantso/js';
interface FieldConfig {
typeInfo: JS.TypeInfo;
label: string;
name: string;
}
function createField(config: FieldConfig) {
const { typeInfo, label, name } = config;
return {
...config,
validate: (value: unknown) => {
const expectedType = typeInfo[name];
return JS.is(expectedType as any, value);
},
render: () => {
// Render input based on type
const type = typeInfo[name];
switch (type) {
case 'string': return `<input type="text" name="${name}" />`;
case 'number': return `<input type="number" name="${name}" />`;
case 'boolean': return `<input type="checkbox" name="${name}" />`;
default: return `<input name="${name}" />`;
}
}
};
}
const emailField = createField({
typeInfo: { email: 'string' },
label: 'Email',
name: 'email'
});
console.log(emailField.validate('test@example.com')); // true
console.log(emailField.validate(123)); // falseRequirements
- Node.js 12.0 or higher
- TypeScript 4.0 or higher (for TypeScript projects)
Dependencies
@avstantso/core- Core utilities and type definitions@avstantso/std-ext- Standard library extensions@avstantso/ts- TypeScript type utilities
License
MIT - See LICENSE file for details
Repository
GitLab - avstantso-js/cross-platform-utils
Contributing
Contributions are welcome! Please feel free to submit issues or pull requests to the repository.