CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-utility-types

Utility Types Collection for TypeScript providing comprehensive type-level utilities for static type manipulation.

Pending
Overview
Eval results
Files

aliases-guards.mddocs/

Type Aliases and Guards

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

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');

Type Aliases

Primitive

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;
}

Falsy

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'

Falsey (Deprecated)

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;
}

Nullish

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: false

Type Guard Functions

isPrimitive

Runtime 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 };
}

isFalsy

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>);
}

isNullish

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;
}

Combined Usage Examples

Type-Safe Data Processing Pipeline

// 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;
}

Type Guard Composition

// 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 Functions

getReturnOfExpression (Deprecated)

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

docs

aliases-guards.md

flow-utilities.md

index.md

mapped-types.md

tile.json