Get Started with Kotlinify

Transform your TypeScript code with Kotlin's most powerful patterns in under 5 minutes

Installation

npm install kotlinify-ts

Or with yarn, pnpm, or bun:

yarn add kotlinify-ts
pnpm add kotlinify-ts
bun add kotlinify-ts

Why Kotlinify?

See the power of functional programming patterns in action

Without Kotlinify

// Nested, hard-to-follow logic
let data = fetchData();
if (data) {
  data = data.filter(x => x.active);
  console.log(`Processing ${data.length}`);
  data = data.map(x => x.value);
  metrics.record('processed', data.length);
  return data;
}
return null;

With Kotlinify

// Clean, chainable, functional - ONE beautiful expression!
await asScope(fetchData())
  .letOrNull(data => data?.filter(x => x.active))
  .also(d => d && console.log(`Processing ${d.length}`))
  .letOrNull(d => d?.map(x => x.value))
  .also(d => d && metrics.record('processed', d.length))
  .value();

Core Patterns

Five powerful concepts that will transform your code

1. Scope Functions - Chain Operations Elegantly

Transform, configure, and observe values through a clean pipeline

import { asScope, apply } from 'kotlinify-ts';

// Transform data through a fluent pipeline - no intermediate variables!
const apiResponse = { data: { user: { name: "Alice", score: 85 } } };

const message = asScope(apiResponse)
  .let(response => response.data.user)
  .let(user => ({ ...user, grade: user.score >= 90 ? 'A' : 'B' }))
  .let(graded => `${graded.name} earned a ${graded.grade}`)
  .value();

console.log(message); // "Alice earned a B"

// Configure complex objects with beautiful chaining
const config = apply({} as any, cfg => {
  cfg.apiUrl = process.env.API_URL;
  cfg.timeout = 5000;
  cfg.retries = 3;
  cfg.headers = { 'Content-Type': 'application/json' };
});

// Track operations with side effects in a clean chain
// Works seamlessly with async data sources too
const processedData = await asScope(fetchData())
  .let(data => data.filter(item => item.active))
  .also(filtered => console.log(`Processing ${filtered.length} items`))
  .let(items => items.map(item => item.value))
  .also(values => metrics.record('items.processed', values.length))
  .value();

2. Null Safety - Handle Nullables Gracefully

Chain operations safely without endless null checks

import { asScope, letOrNull, applyOrNull, takeIf, takeUnless } from 'kotlinify-ts';

// Safely transform nullable values with elegant chaining
const userInput: string | null = getUserInput();

const result = asScope(userInput)
  .letOrNull(input => input.trim())
  .letOrNull(trimmed => trimmed.toLowerCase())
  .letOrNull(normalized => normalizeText(normalized))
  .value();

// Conditional value retention with fluent API
const validEmail = takeIf(email, e => e.includes('@') && e.includes('.'));

const nonEmptyList = takeUnless(items, list => list.length === 0);

// Configure objects only when non-null
const user = applyOrNull(findUserById(id), u => {
  u.lastSeen = Date.now();
  u.sessionCount++;
});

3. Flow - Reactive Streams Made Simple

Build reactive applications with powerful stream processing

import { flowOf, flow, MutableStateFlow } from 'kotlinify-ts/flow';

// Create reactive data streams
const temperatureFlow = flowOf(20, 22, 25, 23, 21)
  .map(celsius => celsius * 9/5 + 32)
  .filter(fahrenheit => fahrenheit > 70)
  .onEach(f => console.log(`Temperature: ${f}°F`));

await temperatureFlow.collect(temp => {
  updateDisplay(temp);
});

// State management with reactive updates
const userState = new MutableStateFlow({ name: "Guest", loggedIn: false });

// Subscribe to state changes
userState
  .map(user => user.name)
  .distinctUntilChanged()
  .collect(name => updateUIName(name));

// Update state triggers all collectors
userState.value = { name: "Alice", loggedIn: true };

4. Sequences - Lazy Evaluation for Performance

Process large datasets efficiently with lazy evaluation

import { asSequence, generateSequence } from 'kotlinify-ts/sequences';

// Process large datasets lazily
const bigData = asSequence(hugeArray)
  .filter(item => item.isValid)
  .map(item => item.normalize())
  .take(100)  // Only processes first 100 valid items
  .toArray();

// Generate infinite sequences
const fibonacci = generateSequence([0, 1], ([a, b]) => [b, a + b])
  .map(([a]) => a)
  .take(10)
  .toArray(); // [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

// Efficient grouping and aggregation
const groups = asSequence(transactions)
  .groupBy(t => t.category); // Returns Map<string, Transaction[]>

const summary = Array.from(groups).map(([category, items]) => ({
  category,
  total: asSequence(items).sumBy(t => t.amount),
  count: items.length
}));

5. Monads - Type-Safe Error Handling

Replace try-catch blocks with composable error handling

import { tryCatch, Success, Failure } from 'kotlinify-ts/monads';
import { Some, None, fromNullable } from 'kotlinify-ts/monads';

// Elegant error handling
const result = tryCatch(() => JSON.parse(jsonString))
  .map(data => data.user)
  .map(user => user.email)
  .fold(
    error => `Failed: ${error.message}`,
    email => `User email: ${email}`
  );

// Null-safe operations with Option
const userOption = fromNullable(findUser(id))
  .filter(u => u.isActive)
  .map(u => u.profile)
  .getOrElse({ name: "Anonymous", avatar: "default.png" });

// Chain fallible operations
const processed = await tryCatchAsync(() => fetchData())
  .flatMap(data => tryCatch(() => validateData(data)))
  .map(valid => transformData(valid))
  .recover(error => getDefaultData());

The Power of Chainable APIs

Enable fluent, readable code with method chaining

Why Chaining Matters

The real value of kotlinify-ts isn't just the individual functions - it's the ability to chain them into expressive, self-documenting pipelines that eliminate intermediate variables and make your intent crystal clear.

Clean, Functional Pipelines

import { asScope } from 'kotlinify-ts';

// Transform any value through a pipeline
const result = asScope(getValue())
  .let(v => process(v))
  .also(v => log(v))
  .let(v => format(v))
  .value();

// No intermediate variables
// No nested callbacks
// Just beautiful, flowing code

✅ Benefits

  • • Fluent, readable pipelines
  • • No intermediate variables
  • • Clear data flow
  • • Easy refactoring
  • • Self-documenting code

🎯 Perfect For

  • • Data transformations
  • • Object configuration
  • • API response processing
  • • Complex business logic
  • • React component props
🎯

Zero Prototype Pollution

Use asScope() for chainable scope functions, or import standalone utilities. No global modifications, no side effects - just clean, explicit imports.

Real-World Example

See how kotlinify-ts transforms a typical data processing pipeline:

import 'kotlinify-ts';

// Process API data with beautiful chaining and error handling
async function processUserData(userId: string) {
  return await fetchUser(userId)
    .tryCatch()
    .map(user => ({
      ...user,
      transactions: user.transactions
        .asSequence()
        .filter(t => t.status === 'completed')
        .map(t => ({ ...t, amount: t.amount * exchangeRate }))
        .sortedBy(t => t.date)
        .take(100)
        .toArray()
    }))
    .also(processed => logger.info('User processed', { userId }))
    .map(user => enrichUserData(user))
    .fold(
      error => {
        logger.error('Failed to process user', { userId, error });
        return getDefaultUser();
      },
      user => {
        cache.set(userId, user);
        return user;
      }
    );
}

// Stream real-time updates
const priceUpdates = flowOf(...initialPrices)
  .flatMapLatest(price =>
    subscribeToUpdates(price.symbol)
      .map(update => ({ ...price, ...update }))
  )
  .distinctUntilChangedBy(p => p.value)
  .throttle(100)
  .onEach(price => updateUI(price))
  .catch(error => handleStreamError(error));

await priceUpdates.collect(price => {
  broadcastToClients(price);
});

What Developers Are Saying

"Finally, I can write TypeScript that's as expressive as my Kotlin code. The scope functions alone have transformed how I structure my React components."

— Senior Full-Stack Developer

"The Flow API is incredible for handling WebSocket streams. It's like RxJS but with a much cleaner API."

— Real-time Applications Developer

"Result and Option monads have eliminated 90% of our null pointer exceptions. Our error handling is now type-safe and composable."

— Tech Lead

Ready to Transform Your Code?

Join the Community

Start Writing Better TypeScript Today

Join thousands of developers who are already using kotlinify-ts to write cleaner, more maintainable TypeScript code with the power of Kotlin's best patterns.