smithery/Higashi-Kota

type-patterns

Type-safe patterns using Result types, discriminated unions, and exhaustive matching. Reference for implementing immutable, type-safe core logic without exceptions.

Installation

$ npx skills add smithery/Higashi-Kota --skill type-patterns

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from smithery/Higashi-Kota.

npx skills add smithery/Higashi-Kota

Browse all from smithery/Higashi-Kota

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 4,874 B
  • docs SUMMARY.md 186 B

History

  1. First recorded snapshot · 0 installs

SKILL.md

Type Safety Patterns

Core Rules

Avoid Use Instead
any Proper type definitions
as type assertions Discriminated unions
throw exceptions Result type
Type guards ts-pattern .exhaustive()

Result Type

export type Result<T, E> =
  | { readonly type: "success"; readonly data: T }
  | { readonly type: "error"; readonly error: E }

// Domain-specific error types
export type AppError =
  | { type: "notFound"; resource: string }
  | { type: "validation"; field: string; message: string }
  | { type: "permission"; action: string }
  | { type: "network"; status: number }
  | { type: "unknown"; cause: unknown }

ts-pattern Exhaustive Matching

import { match } from "ts-pattern"

const handleResult = <T>(result: Result<T, AppError>) =>
  match(result)
    .with({ type: "success" }, ({ data }) => renderData(data))
    .with({ type: "error" }, ({ error }) =>
      match(error)
        .with({ type: "notFound" }, () => showNotFound())
        .with({ type: "validation" }, ({ message }) => showValidation(message))
        .with({ type: "permission" }, () => showPermissionError())
        .with({ type: "network" }, ({ status }) => showNetworkError(status))
        .with({ type: "unknown" }, () => showUnknown())
        .exhaustive()
    )
    .exhaustive()

Discriminated Union Pattern

// Base interface with discriminant
interface BaseNode {
  readonly id: string
  readonly name: string
}

// Discriminated variants
interface FileNode extends BaseNode {
  readonly type: "file"
  readonly size: number
  readonly mimeType: string
}

interface FolderNode extends BaseNode {
  readonly type: "folder"
  readonly children: ReadonlyArray<TreeNode>
}

export type TreeNode = FileNode | FolderNode

// Exhaustive handling
const getIcon = (node: TreeNode) =>
  match(node)
    .with({ type: "file" }, () => FileIcon)
    .with({ type: "folder" }, () => FolderIcon)
    .exhaustive()

Immutable Class Pattern

export class SelectionManager<T extends { id: string }> {
  private constructor(
    private readonly selectedIds: ReadonlySet<string>,
    private readonly anchorId: string | null
  ) {}

  static empty<T extends { id: string }>(): SelectionManager<T> {
    return new SelectionManager(new Set(), null)
  }

  select(itemId: string): SelectionManager<T> {
    return new SelectionManager(new Set([itemId]), itemId)
  }

  toggle(itemId: string): SelectionManager<T> {
    const next = new Set(this.selectedIds)
    if (next.has(itemId)) {
      next.delete(itemId)
    } else {
      next.add(itemId)
    }
    return new SelectionManager(next, itemId)
  }

  isSelected(itemId: string): boolean {
    return this.selectedIds.has(itemId)
  }
}

Readonly Collections

// Prefer readonly variants
type Items = ReadonlyArray<Item>
type Lookup = ReadonlyMap<string, Item>
type UniqueIds = ReadonlySet<string>

// Immutable updates
const addItem = (items: ReadonlyArray<Item>, item: Item): ReadonlyArray<Item> =>
  [...items, item]

const removeItem = (items: ReadonlyArray<Item>, id: string): ReadonlyArray<Item> =>
  items.filter(item => item.id !== id)

Branded Types

// Prevent type confusion
declare const brand: unique symbol

type UserId = string & { readonly [brand]: "UserId" }
type PostId = string & { readonly [brand]: "PostId" }

const createUserId = (id: string): UserId => id as UserId
const createPostId = (id: string): PostId => id as PostId

// Compile-time safety
declare function getUser(id: UserId): User
declare function getPost(id: PostId): Post

const userId = createUserId("123")
const postId = createPostId("456")

getUser(userId)  // ✅ OK
getUser(postId)  // ❌ Type error

Option Type (for nullable values)

export type Option<T> =
  | { readonly type: "some"; readonly value: T }
  | { readonly type: "none" }

export const some = <T>(value: T): Option<T> => ({ type: "some", value })
export const none: Option<never> = { type: "none" }

// Usage
const findUser = (id: string): Option<User> =>
  users.has(id) ? some(users.get(id)!) : none

const userName = match(findUser("123"))
  .with({ type: "some" }, ({ value }) => value.name)
  .with({ type: "none" }, () => "Unknown")
  .exhaustive()

References