Provides guidance and pattern matching for transitioning imperative or OOP code to functional TypeScript paradigms.

Install

mkdir -p .claude/skills/functype && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/14699" && unzip -o skill.zip -d .claude/skills/functype && rm skill.zip

Installs to .claude/skills/functype

Activation

This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.

Help developers use functype functional programming patterns in their TypeScript projects. Use this skill when converting imperative/OOP code to functional patterns, looking up functype APIs and methods, handling nulls with Option, managing errors with Either/Try, composing effects with IO (lazy effects, typed errors, dependency injection, retry), working with immutable collections like List and Set, running async operations with Task, or making HTTP requests with Http (typed fetch wrapper).
496 chars✓ has a “when” triggerlonger than Claude Code's old 250-char listing cap (fine on current versions)
Advanced

Key capabilities

  • Convert imperative code to functional patterns
  • Handle nullable values using Option types
  • Manage errors with Either or Try types
  • Compose effects with IO for lazy operations
  • Work with immutable collections like List and Set

How it works

This skill helps developers refactor TypeScript code to use functype, a Scala-inspired functional programming library, by providing guidance on Option, Either, Try, List, and other functional types.

Inputs & outputs

You give it
TypeScript code with imperative patterns or null checks
You get back
TypeScript code refactored to use functype functional patterns

When to use functype

  • Converting OOP classes to functional patterns
  • Handling null values using Option types
  • Implementing functional pipe patterns in TS

About this skill

Functype User Guide

Overview

Transform TypeScript code to use functype - a Scala-inspired functional programming library providing type-safe alternatives to null checks, exceptions, and imperative patterns. This skill helps integrate Option, Either, Try, List, IO, Task, and other functional types into projects.

When to Use This Skill

Trigger this skill when users:

  • Convert imperative code to functional patterns
  • Look up functype APIs or methods
  • Handle nullable values or optional chaining
  • Replace try-catch with functional error handling
  • Work with immutable collections
  • Compose side effects, async operations, or dependency injection with IO
  • Run async operations with cancellation or progress tracking (Task)
  • Debug functype code or understand error messages

Quick Start

Installation

npm install functype
# or
pnpm add functype

Core Imports

// Import from main bundle
import { Option, Either, Left, Right, Try, List, IO, Tag, Task, Layer } from "functype"

Constructor vs Companion Methods

Functype collections provide multiple ways to create instances:

MethodUse WhenExample
List([...])Creating from existing arrayList(existingArray)
List.of(...)Inline literal valuesList.of(1, 2, 3)
List.empty()Empty collections (typed)List.empty<number>()

Decision Guide

Use Constructor List([...]) when:

  • Converting existing arrays: List(data.items)
  • Spreading iterables: List([...set])
  • Variables holding arrays: List(myArray)

Use List.of(...) when:

  • Inline literal values: List.of(1, 2, 3)
  • Cleaner for small fixed lists: List.of("a", "b", "c")
  • No need to wrap in array brackets

Use List.empty() when:

  • Starting with empty collection: List.empty<User>()
  • Type parameter needed but no initial values
  • Returns singleton (efficient for repeated calls)

Examples

// Constructor - wrapping existing data
const users = List(fetchedUsers)
const items = List([...existingSet])

// .of() - inline literals
const colors = List.of("red", "green", "blue")
const primes = Set.of(2, 3, 5, 7, 11)

// .empty() - typed empty collections
const errors = List.empty<string>()
const cache = Map.empty<string, User>()

Pattern Conversion Guide

Null/Undefined Checks → Option

Before (Imperative):

if (value !== null && value !== undefined) {
  return value.toUpperCase()
}
return ""

After (Functype):

Option(value)
  .map((v) => v.toUpperCase())
  .orElse("")

Optional Chaining → Option Chain

Before:

const url = user?.profile?.avatar?.url

After:

const url = Option(user)
  .flatMap((u) => Option(u.profile))
  .flatMap((p) => Option(p.avatar))
  .map((a) => a.url)
  .orElse("/default-avatar.png")

Try-Catch → Try or Either

Before:

try {
  return JSON.parse(str)
} catch (e) {
  return null
}

After (with Try):

Try(() => JSON.parse(str))
  .toOption()
  .orElse(null)

After (with Either):

Try(() => JSON.parse(str))
  .toEither()
  .fold(
    (error) => `Parse failed: ${error.message}`,
    (data) => data,
  )

Array Operations → List

Before:

array.filter((x) => x > 0).map((x) => x * 2)

After:

List(array)
  .filter((x) => x > 0)
  .map((x) => x * 2)
  .toArray()

If-Else Chains → Cond

Before:

if (x > 10) {
  return "big"
} else if (x > 5) {
  return "medium"
} else {
  return "small"
}

After:

import { Cond } from "functype"

Cond.start<string>()
  .case(x > 10, "big")
  .case(x > 5, "medium")
  .otherwise("small")

Switch Statements → Match

Before:

switch (status) {
  case "success":
    return data
  case "error":
    return null
  default:
    return undefined
}

After:

import { Match } from "functype"

Match(status)
  .case("success", () => data)
  .case("error", () => null)
  .exhaustive()

Common Use Cases

Validation with Either

import { Either, Left, Right } from "functype"

function validateEmail(email: string): Either<string, string> {
  return email.includes("@") ? Right(email) : Left("Invalid email format")
}

function validateUser(user: any): Either<string, User> {
  return validateEmail(user.email)
    .map((email) => ({ ...user, email }))
    .flatMap((u) => (u.age >= 18 ? Right(u) : Left("Must be 18 or older")))
}

const result = validateUser({ email: "[email protected]", age: 20 }).fold(
  (error) => console.error(error),
  (user) => console.log("Valid user:", user),
)

Safe API Calls with Option

import { Option } from "functype"

interface User {
  id: string
  name: string
  email?: string
}

function getUserEmail(userId: string): Option<string> {
  return Option(fetchUser(userId))
    .flatMap((user) => Option(user.email))
    .filter((email) => email.includes("@"))
}

const email = getUserEmail("123").orElse("[email protected]")

Error Recovery with Try

import { Try } from "functype"

const parseConfig = Try(() => JSON.parse(configStr))
  .recover((error) => {
    console.warn("Using default config:", error)
    return defaultConfig
  })
  .map((config) => validateConfig(config))

Collection Pipeline with List

import { List } from "functype"

const users = List([
  { name: "Alice", hobbies: ["reading", "coding"] },
  { name: "Bob", hobbies: ["gaming", "music"] },
])

const allHobbies = users
  .flatMap((user) => List(user.hobbies))
  .toSet() // Remove duplicates
  .toArray()

Additional Data Structures

IO<R,E,A> - Effect Type

IO is a lazy, composable effect type with typed errors and dependency injection:

  • R = Requirements (environment/dependencies needed)
  • E = Error type (typed failures)
  • A = Success type (value produced on success)
import { IO, Tag, Layer } from "functype"

// Creation
IO.sync(() => computation()) // Sync operation
IO.succeed(value) // Pure success
IO.fail(error) // Pure failure
IO.async(() => promise) // Async operation
IO.tryPromise({
  // Promise with error mapping
  try: () => fetch(url),
  catch: (e) => new NetworkError(e),
})

// Dependency injection
const Database = Tag<DatabaseService>("Database")
const dbEffect = IO.service(Database) // Access a service
const program = dbEffect.flatMap((db) => IO.sync(() => db.query()))
program.provide(Layer.fromValue(Database, myDb)) // Provide deps

// Generator do-notation (cleaner syntax)
const program = IO.gen(function* () {
  const db = yield* IO.service(Database)
  const user = yield* IO.tryPromise(() => db.findUser(id))
  return user
})

// Execution
await effect.run() // Returns Promise<A>
effect.runSync() // Returns A (throws if async)
await effect.runEither() // Returns Promise<Either<E,A>>
await effect.runExit() // Returns Promise<Exit<E,A>>

// run() vs runExit(): Either has two branches, Exit has four. Use run() when all you
// need is success-or-not; use runExit() to tell a typed failure from a *defect* (a
// value that is not an E — a throwing IO.sync thunk or map/mapError callback, or
// IO.die) or from an interruption. run() puts both in the Left, raw.
const exit = await effect.runExit()
exit.isSuccess() // completed with a value
exit.isFailure() // a value from the declared E channel
exit.isDie() // a defect — NOT an E
exit.isInterrupted() // cancelled

// Both extra handlers are optional and fall back to the failure branch, so existing
// three-argument fold / three-key match calls keep working.
exit.fold(onFailure, onSuccess, onInterrupted?, onDie?)
exit.match({ Success, Failure, Interrupted, Die? })

// Defects stay recoverable: recover / recoverWith / fold / mapError treat a Die exactly
// as a Failure. Exit.Die records what the outcome was when nothing recovered it.

Tuple - Type-safe Fixed-length Array

import { Tuple } from "functype"

const pair = Tuple(42, "hello")
pair.first() // 42
pair.second() // "hello"
pair.mapFirst((x) => x * 2) // Tuple(84, "hello")
pair.swap() // Tuple("hello", 42)
pair.apply((a, b) => a + b.length) // 47
pair.concat(Tuple(true)) // Tuple(42, "hello", true)

Stack - Last-In-First-Out Collection

import { Stack } from "functype"

Stack.empty<number>()
const stack = Stack.of(1, 2, 3)
stack.push(value) // Returns new Stack
stack.pop() // Returns [Option<T>, Stack<T>]
stack.peek() // Returns Option<T>
stack.match({
  Empty: () => "empty stack",
  NonEmpty: (top, rest) => `top: ${top}`,
})

LazyList - Lazy-evaluated List

import { LazyList } from "functype"

// Creation
LazyList([1, 2, 3])
LazyList.of(1, 2, 3)
LazyList.empty<number>()

// Infinite sequences
const naturals = LazyList.from(0, (n) => n + 1)
const evens = naturals.filter((n) => n % 2 === 0).take(10)

// Operations are deferred until needed
const result = LazyList(hugeArray)
  .filter((x) => x > 0)
  .map((x) => x * 2)
  .take(5)
  .toArray() // Only processes first 5 matching elements

Do-Notation (Generator Comprehensions)

Scala-like for-comprehensions using JavaScript generators:

import { Do, DoAsync, $ } from "functype"

// Synchronous comprehensions
const result = Do(function* () {
  const x = yield* $(Option(5))
  const y = yield* $(Option(10))
  return x + y
}) // Option(15)

// Async comprehensions
const asyncResult = await DoAsync(async function* () {
  const user = yield* $(await fetchUserAsync(userId))
  const profile = yield* $(await fetchProfileAsync(user.id))
  return { user, profile }
})

// Cartesian products with List (2.5x-12x faster than nested flatMap)
const pairs = Do(function* () {
  con

---

*Content truncated.*

When not to use it

  • When the user prefers imperative or OOP patterns
  • When the task does not involve functional programming concepts

Prerequisites

functype

Limitations

  • Requires installation of the functype library.
  • Focuses on Scala-inspired functional programming patterns.
  • Requires understanding of functional programming concepts.

How it compares

This skill provides specific guidance and examples for converting common imperative patterns into type-safe functional constructs using functype, offering a structured approach to adopting functional programming in TypeScript.

Compared to similar skills

functype side by side with the closest alternatives in the catalog.

SkillInstallsUpdatedSafetyDifficulty
functype (this skill)01moReviewAdvanced
typescript-expert106moReviewAdvanced
agent-coder36moNo flagsIntermediate
functional32moNo flagsIntermediate

Try saying

Example prompts that trigger this skill in your AI assistant.

Search skills

Search the agent skills registry