Skip to content
Back to skills

Typescript

ASecurity

Tommy's TypeScript style rules and philosophy: simple explicit code in the style of Mario Zechner (badlogic) and Anthony Fu (antfu), few abstractions and little indirection, named constants instead of magic numbers, AbortSignal on every I/O, fetch or sleep operation, object params instead of 2+ positional args, and type-safe errors as values with errore. ALWAYS load this skill before writing, refactoring or reviewing TypeScript code.

  • 44 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added November 7, 2026
code-qualitytypescriptgorefactoringapi

Works with

  • api

Security analysis

A100/100

Scanned November 7, 2026

npx -y skills add remorses/opencode-config --skill typescript --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Typescript?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Typescript
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/remorses-typescript/badge)](https://www.skillsdirectory.com/skills/remorses-typescript)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: typescript
description: >
  Tommy's TypeScript style rules and philosophy: simple explicit code in the style of
  Mario Zechner (badlogic) and Anthony Fu (antfu), few abstractions and little
  indirection, named constants instead of magic numbers, AbortSignal on every I/O,
  fetch or sleep operation, object params instead of 2+ positional args, and type-safe
  errors as values with errore. ALWAYS load this skill before writing, refactoring or
  reviewing TypeScript code.
---

# typescript

Simple code a reader understands top to bottom in one pass. Every failure mode is visible
in the return type. Also load the `errore` skill: it is the error convention.

## Philosophy

Write TypeScript in the style of **Mario Zechner** (badlogic, creator of pi mono) and
**Anthony Fu** (antfu). Simple, explicit, a few but elegant abstractions where they are
really worth it.

- **Few abstractions.** Only when they remove real duplication or complexity. Three similar
  lines beat a premature helper.
- **Little indirection.** Plain functions and objects. No jumping through files, factories,
  registries or class hierarchies to find what a call does.
- **Explicit over clever.** No metaprogramming, proxies, decorators or type gymnastics
  unless the gain is large.
- **Architecture first.** Know each system boundary; put logic at the right layer instead of
  patching around a flaw.
- **Flat control flow.** Early returns, no `else` chains, no nested `if`.
- **Derive, do not cache.** Minimize mutable state; compute from the source of truth.

## No magic numbers

Name every number in an `UPPER_SNAKE_CASE` constant at the top of the file, unit in the
name, numeric separators for large values. Same for repeated literal strings.

```ts
const REQUEST_TIMEOUT_MS = 30_000
const MAX_UPLOAD_BYTES = 10 * 1024 * 1024
const HTTP_NOT_FOUND = 404
```

- No constant needed for `0`, `1`, `-1` index math, `* 2`, `% 2`.
- Shared constants go next to the owning module or in one `constants.ts`, never one file
  per value.

## Object params

Two or more params means one object, so call sites use named arguments. Inline the param
type in the signature; extract a named type only if other code uses it. Defaults go in the
destructuring.

```ts
await copyFile({ from: src, to: dest, overwrite: true, signal })
```

- One positional arg is fine when obvious: `slugify(title)`.
- Callbacks for `.map` and library APIs keep the shape the API requires.

## AbortSignal on every long running action

Any function doing I/O, `fetch`, child processes, `sleep`, polling or long timeouts MUST
take a **required** `signal` in its object args and pass it to every async call inside.
The caller owns the `AbortController`. An optional signal gets forgotten and silently
breaks cancellation in one layer.

```ts
const user = await getUser({ id, signal: controller.signal })
```

**Timeouts:** use `AbortSignal.any` + `AbortSignal.timeout`, never manual `setTimeout` +
`abort()`.

```ts
const res = await fetch(url, {
  signal: AbortSignal.any([signal, AbortSignal.timeout(REQUEST_TIMEOUT_MS)]),
}).catch((e) => new NetworkError({ url, cause: e }))
if (errore.isAbortError(res)) return res
if (res instanceof Error) return res
```

**Loops:** check `signal.aborted` before each expensive step.

```ts
for (const file of files) {
  if (signal.aborted) return new JobAbortedError({ cause: signal.reason })
  const result = await uploadFile({ file, signal })
  if (result instanceof Error) return result
}
```

**Sleep:** never `new Promise((r) => setTimeout(r, ms))`. Use a sleep that stops on abort:

```ts
export function sleep({ ms, signal }: { ms: number; signal: AbortSignal }) {
  return new Promise<SleepAbortedError | void>((resolve) => {
    const onAbort = () => {
      clearTimeout(timer)
      resolve(new SleepAbortedError({ cause: signal.reason })) // extends errore.AbortError
    }
    const timer = setTimeout(() => {
      signal.removeEventListener('abort', onAbort)
      resolve()
    }, ms)
    if (signal.aborted) return onAbort()
    signal.addEventListener('abort', onAbort, { once: true })
  })
}

await sleep({ ms: RETRY_DELAY_MS, signal: controller.signal })
```

## Type-safe errors with errore

Follow the `errore` skill. Errors are known and each function's failure modes are explicit
in its inferred return type, e.g. `Promise<UserNotFoundError | NetworkError | User>`.

- Return errors, never throw expected failures.
- Convert exceptions only at boundaries (`.catch()` async, `errore.try` sync), wrapped in a
  tagged error with `cause`.
- Abort errors extend `errore.AbortError`, detected with `errore.isAbortError`.
- Use `errore.matchError` when callers handle each failure mode differently.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.