Utility Types Collection for TypeScript providing comprehensive type-level utilities for static type manipulation.
—
Type aliases and guards provide common type definitions with corresponding runtime type guard functions for type checking at runtime. These utilities bridge compile-time type safety with runtime type validation.
import {
// Type aliases
Primitive, Falsy, Falsey, Nullish,
// Type guard functions
isPrimitive, isFalsy, isNullish
} from 'utility-types';For CommonJS:
const {
// Type aliases
Primitive, Falsy, Falsey, Nullish,
// Type guard functions
isPrimitive, isFalsy, isNullish
} = require('utility-types');Represents JavaScript primitive types: string, number, bigint, boolean, symbol, null, and undefined.
type Primitive = string | number | bigint | boolean | symbol | null | undefined;Usage:
// Type checking
function isSimpleValue(value: unknown): value is Primitive {
return value !== Object(value);
}
// Excluding complex types
type SimpleData<T> = T extends Primitive ? T : never;
type Example1 = SimpleData<string>; // Result: string
type Example2 = SimpleData<object>; // Result: never
// Filtering object properties
interface MixedData {
id: number;
name: string;
settings: object;
callback: () => void;
isActive: boolean;
metadata: Record<string, any>;
}
type PrimitiveFields = {
[K in keyof MixedData]: MixedData[K] extends Primitive ? K : never;
}[keyof MixedData];
// Result: 'id' | 'name' | 'isActive'
type PrimitiveData = Pick<MixedData, PrimitiveFields>;
// Result: { id: number; name: string; isActive: boolean; }
// Practical usage in serialization
function serializePrimitives<T extends Record<string, any>>(obj: T): Pick<T, {
[K in keyof T]: T[K] extends Primitive ? K : never;
}[keyof T]> {
const result = {} as any;
for (const [key, value] of Object.entries(obj)) {
if (value !== Object(value)) {
result[key] = value;
}
}
return result;
}Represents JavaScript falsy values: false, empty string, 0, null, and undefined.
type Falsy = false | '' | 0 | null | undefined;Usage:
// Remove falsy values from union types
type TruthyString = Exclude<string, Falsy>;
// Result: string (but excludes empty string)
type TruthyNumber = Exclude<number, Falsy>;
// Result: number (but excludes 0)
// Filter arrays to remove falsy values
function filterTruthy<T>(array: T[]): Array<Exclude<T, Falsy>> {
return array.filter(Boolean) as Array<Exclude<T, Falsy>>;
}
const mixed = [1, 0, 'hello', '', true, false, null, undefined];
const truthy = filterTruthy(mixed);
// Type: Array<1 | 'hello' | true>
// Value: [1, 'hello', true]
// Conditional type based on falsy check
type IsTruthy<T> = T extends Falsy ? false : true;
type Test1 = IsTruthy<0>; // Result: false
type Test2 = IsTruthy<42>; // Result: true
type Test3 = IsTruthy<''>; // Result: false
type Test4 = IsTruthy<'hello'>; // Result: true
// Default value patterns
function withDefault<T>(value: T | Falsy, defaultValue: T): T {
return value || defaultValue;
}
const result1 = withDefault('', 'default'); // Result: 'default'
const result2 = withDefault('value', 'default'); // Result: 'value'Deprecated alias for Falsy. Provided for backward compatibility until v4.
type Falsey = Falsy;Usage:
// Deprecated - use Falsy instead
type OldFalseyType = Falsey;
// Preferred
type NewFalsyType = Falsy;
// Migration guide
function migrationExample(value: unknown): value is Falsy {
// Old: value is Falsey
// New: value is Falsy
return !value;
}Represents nullish values in TypeScript: null and undefined.
type Nullish = null | undefined;Usage:
// Nullish coalescing patterns
function withNullishDefault<T>(value: T | Nullish, defaultValue: T): T {
return value ?? defaultValue;
}
const result1 = withNullishDefault(null, 'default'); // Result: 'default'
const result2 = withNullishDefault(undefined, 'default'); // Result: 'default'
const result3 = withNullishDefault('', 'default'); // Result: '' (empty string is not nullish)
const result4 = withNullishDefault(0, 42); // Result: 0 (zero is not nullish)
// Remove nullish values from union types
type NonNullishString = Exclude<string | null | undefined, Nullish>;
// Result: string
// Optional property handling
interface Config {
host?: string;
port?: number;
timeout?: number;
}
type RequiredConfig = {
[K in keyof Config]-?: Exclude<Config[K], Nullish>;
};
// Result: { host: string; port: number; timeout: number; }
// Array filtering
function filterNullish<T>(array: Array<T | Nullish>): T[] {
return array.filter((item): item is T => item != null);
}
const withNulls = [1, null, 2, undefined, 3];
const withoutNulls = filterNullish(withNulls);
// Type: number[]
// Value: [1, 2, 3]
// Conditional types
type IsNullish<T> = T extends Nullish ? true : false;
type Test1 = IsNullish<null>; // Result: true
type Test2 = IsNullish<undefined>; // Result: true
type Test3 = IsNullish<0>; // Result: false
type Test4 = IsNullish<''>; // Result: falseRuntime type guard to check if a value is a primitive type.
function isPrimitive(val: unknown): val is Primitive;Usage:
// Basic usage
const value1: unknown = 'hello';
const value2: unknown = { name: 'John' };
const value3: unknown = [1, 2, 3];
if (isPrimitive(value1)) {
// value1 is now typed as Primitive
console.log(typeof value1); // Safe to use
}
if (isPrimitive(value2)) {
// This block won't execute - objects are not primitive
} else {
// value2 is unknown but we know it's not primitive
console.log('Complex type');
}
// Filtering arrays
const mixedArray: unknown[] = [1, 'hello', {}, [], true, null, undefined, Symbol('test')];
const primitives = mixedArray.filter(isPrimitive);
// Type: Primitive[]
// Contains: [1, 'hello', true, null, undefined, Symbol('test')]
const nonPrimitives = mixedArray.filter(item => !isPrimitive(item));
// Type: unknown[] (but we know they're not primitive)
// Contains: [{}, []]
// Type-safe serialization
function safeSerialization(data: Record<string, unknown>): Record<string, Primitive> {
const result: Record<string, Primitive> = {};
for (const [key, value] of Object.entries(data)) {
if (isPrimitive(value)) {
result[key] = value; // Type-safe assignment
}
}
return result;
}
// Form data processing
function processFormData(formData: Record<string, unknown>) {
const primitiveData: Record<string, Primitive> = {};
const complexData: Record<string, unknown> = {};
for (const [key, value] of Object.entries(formData)) {
if (isPrimitive(value)) {
primitiveData[key] = value;
} else {
complexData[key] = value;
}
}
return { primitiveData, complexData };
}Runtime type guard to check if a value is falsy.
function isFalsy(val: unknown): val is Falsy;Usage:
// Basic usage
const values = [0, '', false, null, undefined, 'hello', 1, true, {}];
const falsyValues = values.filter(isFalsy);
// Type: Falsy[]
// Contains: [0, '', false, null, undefined]
const truthyValues = values.filter(val => !isFalsy(val));
// Type: unknown[] (but we know they're truthy)
// Contains: ['hello', 1, true, {}]
// Form validation
function validateFormField(value: unknown): string | null {
if (isFalsy(value)) {
return 'Field is required';
}
// value is now typed as Exclude<unknown, Falsy>
// We know it's not falsy
return null;
}
// Array compaction
function compact<T>(array: T[]): Array<Exclude<T, Falsy>> {
return array.filter((item): item is Exclude<T, Falsy> => !isFalsy(item));
}
const messyArray = [1, 0, 'test', '', true, false, null, undefined, 'hello'];
const cleanArray = compact(messyArray);
// Type: Array<1 | 'test' | true | 'hello'>
// Value: [1, 'test', true, 'hello']
// Configuration with defaults
interface PartialConfig {
host?: string | null;
port?: number | null;
secure?: boolean | null;
}
function applyConfigDefaults(config: PartialConfig): Required<PartialConfig> {
return {
host: isFalsy(config.host) ? 'localhost' : config.host,
port: isFalsy(config.port) ? 8080 : config.port,
secure: isFalsy(config.secure) ? false : config.secure
};
}
// Conditional rendering helper
function renderIfTruthy<T>(value: T, render: (val: Exclude<T, Falsy>) => JSX.Element): JSX.Element | null {
if (isFalsy(value)) {
return null;
}
return render(value as Exclude<T, Falsy>);
}Runtime type guard to check if a value is nullish (null or undefined).
function isNullish(val: unknown): val is Nullish;Usage:
// Basic usage
const values = [null, undefined, 0, '', false, 'hello', 1, {}];
const nullishValues = values.filter(isNullish);
// Type: Nullish[]
// Contains: [null, undefined]
const nonNullishValues = values.filter(val => !isNullish(val));
// Type: unknown[] (but we know they're not nullish)
// Contains: [0, '', false, 'hello', 1, {}]
// Nullish coalescing with type safety
function safeAccess<T>(value: T | Nullish, defaultValue: T): T {
if (isNullish(value)) {
return defaultValue;
}
return value; // TypeScript knows value is T here
}
const result1 = safeAccess(null, 'default'); // Result: 'default'
const result2 = safeAccess(undefined, 'default'); // Result: 'default'
const result3 = safeAccess('value', 'default'); // Result: 'value'
const result4 = safeAccess(0, 42); // Result: 0 (zero is not nullish)
// Optional property processing
interface User {
id: number;
name?: string;
email?: string;
avatar?: string;
}
function processUser(user: User): Required<User> {
return {
id: user.id,
name: isNullish(user.name) ? 'Anonymous' : user.name,
email: isNullish(user.email) ? 'no-email@example.com' : user.email,
avatar: isNullish(user.avatar) ? '/default-avatar.png' : user.avatar
};
}
// Database result processing
type DatabaseResult<T> = T | null | undefined;
function processResults<T>(results: DatabaseResult<T>[]): T[] {
return results.filter((result): result is T => !isNullish(result));
}
const dbResults: DatabaseResult<string>[] = ['user1', null, 'user2', undefined, 'user3'];
const validResults = processResults(dbResults);
// Type: string[]
// Value: ['user1', 'user2', 'user3']
// API response handling
interface ApiResponse<T> {
data?: T;
error?: string;
status: number;
}
function extractData<T>(response: ApiResponse<T>): T | null {
if (response.status !== 200) {
return null;
}
if (isNullish(response.data)) {
return null;
}
return response.data; // TypeScript knows data is T here
}
// Form data validation
function validateNonNullishFields<T extends Record<string, any>>(
data: T
): Record<keyof T, Exclude<T[keyof T], Nullish>> | string[] {
const errors: string[] = [];
const validated = {} as any;
for (const [key, value] of Object.entries(data)) {
if (isNullish(value)) {
errors.push(`${key} is required`);
} else {
validated[key] = value;
}
}
return errors.length > 0 ? errors : validated;
}// Comprehensive data processing using all utilities
interface RawData {
id?: number | null;
name?: string | null;
email?: string | null;
score?: number | null;
metadata?: Record<string, unknown> | null;
}
function processRawData(items: RawData[]): ProcessedData[] {
return items
.filter(item => !isNullish(item))
.map(item => {
// Extract primitive fields
const primitiveFields: Record<string, Primitive> = {};
const complexFields: Record<string, unknown> = {};
for (const [key, value] of Object.entries(item)) {
if (!isNullish(value)) {
if (isPrimitive(value)) {
primitiveFields[key] = value;
} else {
complexFields[key] = value;
}
}
}
return {
id: primitiveFields.id ?? 0,
name: isFalsy(primitiveFields.name) ? 'Unknown' : primitiveFields.name as string,
email: isFalsy(primitiveFields.email) ? null : primitiveFields.email as string,
score: primitiveFields.score ?? 0,
hasMetadata: !isNullish(complexFields.metadata)
};
});
}
interface ProcessedData {
id: number;
name: string;
email: string | null;
score: number;
hasMetadata: boolean;
}// Combining type guards for complex validation
function isValidPrimitive(value: unknown): value is Exclude<Primitive, Nullish | Falsy> {
return isPrimitive(value) && !isNullish(value) && !isFalsy(value);
}
function isValidString(value: unknown): value is string {
return typeof value === 'string' && !isFalsy(value);
}
function isValidNumber(value: unknown): value is number {
return typeof value === 'number' && !isFalsy(value) && !isNaN(value);
}
// Usage in validation schemas
interface ValidationSchema {
[key: string]: (value: unknown) => boolean;
}
const userValidation: ValidationSchema = {
id: isValidNumber,
name: isValidString,
email: (value): value is string =>
isValidString(value) && /\S+@\S+\.\S+/.test(value),
age: (value): value is number =>
isValidNumber(value) && value >= 0 && value <= 150
};
function validateUser(userData: Record<string, unknown>): boolean {
return Object.entries(userValidation).every(([key, validator]) =>
validator(userData[key])
);
}Deprecated function for inferring return types from expressions. Use TypeScript's built-in ReturnType<T> or $Call API instead.
function getReturnOfExpression<RT>(expression: (...params: any[]) => RT): RT;Usage:
// DEPRECATED - Do not use in new code
import { getReturnOfExpression } from 'utility-types';
const increment = () => ({ type: 'INCREMENT' as 'INCREMENT' });
type IncrementType = typeof getReturnOfExpression<typeof increment>;
// Result: { type: "INCREMENT"; }
// PREFERRED - Use these instead:
type IncrementTypeModern1 = ReturnType<typeof increment>;
type IncrementTypeModern2 = $Call<typeof increment>;Migration Guide:
// Old way (deprecated):
const myFunction = (x: number) => ({ result: x * 2 });
type OldReturnType = typeof getReturnOfExpression<typeof myFunction>;
// New way (preferred):
type NewReturnType = ReturnType<typeof myFunction>;
// or
type AlternativeReturnType = $Call<typeof myFunction>;Note: This function was deprecated in TypeScript v2.8 when ReturnType<T> was introduced. It returns undefined at runtime but provides correct type information at compile time.
Install with Tessl CLI
npx tessl i tessl/npm-utility-types