Utils

Essential Kotlin-style utility functions for everyday TypeScript development

Overview

The utils module provides common Kotlin standard library functions adapted for TypeScript. These utilities help with control flow, validation, error handling, lazy initialization, and functional programming patterns.

Control Flow

Repeat operations and loop control

import { repeat, repeatAsync } from 'kotlinify-ts'

// Execute action n times (sync)
repeat(5, (index) => {
  console.log(`Iteration ${index}`)
})
// Prints: Iteration 0, 1, 2, 3, 4

// Execute async action n times
await repeatAsync(3, async (index) => {
  await someAsyncOperation(index)
})

Runtime Validation

Require and check with type narrowing

import { require, check, requireNotNull, checkNotNull } from 'kotlinify-ts'

function processAge(age: number) {
  // Throws if condition is false
  require(age >= 0, 'Age must be non-negative')
  require(age < 150, () => `Age ${age} is unrealistic`)

  // TypeScript knows age is valid here
  return age * 365
}

function processUser(user: User | null) {
  // Returns non-null or throws
  const validUser = requireNotNull(user, 'User is required')

  // TypeScript knows validUser is User (not null)
  return validUser.name
}

// check is similar to require
function validateConfig(config: Config) {
  check(config.port > 0, 'Port must be positive')
  check(config.timeout > 0, 'Timeout must be positive')
}

Error Handling

Catch exceptions safely

import { runCatching, runCatchingAsync, TODO } from 'kotlinify-ts'

// Catch exceptions and return result object
const result = runCatching(() => {
  return riskyOperation()
})

if (result.success) {
  console.log('Value:', result.value)
} else {
  console.error('Error:', result.error)
}

// Async version
const asyncResult = await runCatchingAsync(async () => {
  return await fetchData()
})

// Mark unimplemented code
function futureFeature() {
  TODO('Implement this feature')
  // Throws: Error: Not implemented: Implement this feature
}

Lazy Initialization

Compute values only when needed

import { lazy } from 'kotlinify-ts'

// Create lazy value (computed on first access)
const expensiveConfig = lazy(() => {
  console.log('Computing config...')
  return loadConfigFromFile()
})

// Not computed yet
console.log('Before access')

// Computed now (prints "Computing config...")
const config1 = expensiveConfig()

// Uses cached value (doesn't print again)
const config2 = expensiveConfig()

console.log(config1 === config2) // true

Performance Measurement

Measure execution time

import { measure, measureSync } from 'kotlinify-ts'

// Measure async function
const { value, duration } = await measure(async () => {
  await heavyComputation()
  return 'result'
})

console.log(`Completed in ${duration}ms`)
console.log(`Result: ${value}`)

// Measure sync function
const result = measureSync(() => {
  let sum = 0
  for (let i = 0; i < 1000000; i++) {
    sum += i
  }
  return sum
})

console.log(`Sum: ${result.value}, Time: ${result.duration}ms`)

Functional Utilities

Common functional programming helpers

import { identity, constant, noop } from 'kotlinify-ts'

// Identity function (returns input)
const numbers = [1, 2, 3].map(identity) // [1, 2, 3]

// Constant function (always returns same value)
const getDefault = constant(42)
getDefault() // 42
getDefault() // 42

// No-op function (does nothing)
const cleanup = shouldCleanup ? doCleanup : noop
cleanup() // Safe to call

Assertions

Development-time checks

import { assert, assertNotNull, error } from 'kotlinify-ts'

function processData(data: unknown[]) {
  // Development assertion
  assert(data.length > 0, 'Data should not be empty')

  const first = data[0]
  const value = assertNotNull(first, 'First element should exist')

  // TypeScript knows value is non-null here
  return value
}

function handleInvalidState(state: string): never {
  switch (state) {
    case 'valid':
      return processValid()
    case 'pending':
      return processPending()
    default:
      // Throw error with message
      error(`Invalid state: ${state}`)
  }
}

Real-World Examples

Configuration Loading

import { lazy, requireNotNull } from 'kotlinify-ts'

const getConfig = lazy(() => {
  const env = process.env.NODE_ENV
  requireNotNull(env, 'NODE_ENV must be set')

  return {
    apiUrl: requireNotNull(
      process.env.API_URL,
      'API_URL must be configured'
    ),
    timeout: parseInt(process.env.TIMEOUT || '5000'),
    env
  }
})

// Config loaded only when first accessed
const config = getConfig()

Input Validation

import { require, requireNotNull } from 'kotlinify-ts'

function createUser(data: {
  name?: string
  email?: string
  age?: number
}) {
  const name = requireNotNull(data.name, 'Name is required')
  const email = requireNotNull(data.email, 'Email is required')
  const age = requireNotNull(data.age, 'Age is required')

  require(name.length >= 2, 'Name must be at least 2 characters')
  require(email.includes('@'), 'Email must be valid')
  require(age >= 18, 'Must be 18 or older')

  return { name, email, age }
}

Safe Computation

import { runCatching, measure } from 'kotlinify-ts'

async function processWithMetrics(data: unknown[]) {
  const result = await measure(async () => {
    return await runCatchingAsync(async () => {
      return await heavyProcessing(data)
    })
  })

  if (!result.value.success) {
    console.error(`Processing failed in ${result.duration}ms`)
    console.error('Error:', result.value.error)
    return null
  }

  console.log(`Processed in ${result.duration}ms`)
  return result.value.value
}

Retry Logic with Utils

import { repeat, runCatchingAsync } from 'kotlinify-ts'

async function retryOperation(maxAttempts: number) {
  let lastError: Error | undefined

  await repeat(maxAttempts, async (attempt) => {
    const result = await runCatchingAsync(async () => {
      return await unstableOperation()
    })

    if (result.success) {
      console.log('Success on attempt', attempt + 1)
      return result.value
    }

    lastError = result.error
    console.log(`Attempt ${attempt + 1} failed`)
  })

  if (lastError) {
    throw lastError
  }
}

API Reference

repeat(times, action)

Execute action n times synchronously

repeatAsync(times, action)

Execute async action n times

require(condition, message?)

Throw if condition is false (type narrowing)

check(condition, message?)

Runtime check assertion

requireNotNull(value, message?)

Return non-null value or throw (type narrowing)

checkNotNull(value, message?)

Check non-null or throw

TODO(message?)

Throw not-implemented error

todo(message?)

Alias for TODO - marks unimplemented code

error(message)

Throw error with message - useful for exhaustive checks

assert(condition, message?)

Development assertion that throws if false

assertNotNull(value, message?)

Assert value is non-null, return it with type narrowing

runCatching(block)

Catch exceptions, return { success, value?, error? }

runCatchingAsync(block)

Async version of runCatching

lazy(initializer)

Lazy initialization with caching

measure(block)

Measure execution time, return { value, duration }

measureSync(block)

Sync version of measure

identity(value)

Return input value unchanged

constant(value)

Return function that always returns value

noop()

No-operation function

Next Steps