typescript-best-practices

Writes strong type definitions that catch mistakes before code runs and make APIs safer to use.

Installation
Run `npx skills add "https://github.com/christofferbergj/agent-skills" --skill "typescript-best-practices"` to install this skill, then follow its SKILL.md instructions for my next request.

Paste this into Claude Code, Cursor, or any agent that can run commands.

SKILL.mdShow the author's original SKILL.md
---
name: typescript-best-practices
description: "Use in a Pstack task when writing TypeScript types, validation boundaries, discriminated unions, or public APIs."
---

# TypeScript best practices

Apply the /principle-type-system-discipline first.

| Rule                  | Summary                                                                                                                                                                             |
| --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Discriminated unions  | Model variants with a `kind` literal discriminant so impossible states can't be represented. No optional-field bags.                                                                |
| Branded types         | Brand primitives with `& { readonly __brand: "X" }` so they can't be mixed up. Validate once at the boundary.                                                                       |
| Constructive modeling | Build the shape so the illegal value can't be constructed. `[T, ...T[]]` for non-empty, `[T, T][]` for even length. Numeric range constraints still require boundary validation.    |
| Simplest total type   | Keep `T[]` while every operation on it stays total. Strengthen to `NonEmpty<T>` only where the loose type forces `!`, a cast, or a "should never happen" throw.                     |
| `unknown` over `any`  | External data is `unknown`.                                                                                                                                                         |
| Schemas before guards | Before hand-writing a property-by-property type guard, use the repository's runtime schema library and infer the type from the schema, such as `z.infer`.                           |
| Unchecked assertions  | Avoid assertions that bypass validation. `as const` preserves literal types; a validated constructor may assert a brand.                                                            |
| Narrowing hierarchy   | Discriminant switch > `in` operator > `typeof`/`instanceof` > user-defined type guard > `as`.                                                                                       |
| Type guards           | Must verify the claim. A lying guard is worse than `as` because the bug hides behind a name that says it's safe. Name them `isX` or `hasX`.                                         |
| Exhaustiveness        | Inline `const _exhaustive: never = x;` in default arms so the compiler errors when a new variant is added.                                                                          |
| `satisfies` over `as` | Checks conformance without annotating the value as the target type. Contextual typing still affects literal inference.                                                              |
| Boundary validation   | Parse where data crosses in, into a named domain type. `Record<string, unknown>` (however spelled) stops at that parse. Trust types inside. See the /principle-boundary-discipline. |
| Schema-derived types  | Reach for `Pick`/`Omit`/`Parameters`/`ReturnType`/`Awaited`/`typeof` before declaring a new interface.                                                                              |
| Object args           | Pass objects, not positional, so argument order is self-documenting. Skip on hot paths (per-frame render, tokenizers, parsers).                                                     |
| Real tests            | Don't mock what you can run. Prefer the framework's real test primitives with leak/disposable checks, and verify UI in a running build. Mock only what you can't run locally.       |
| Structured telemetry  | Prefer structured logger diagnostics with enough context to debug from an id. No `console.log` in shipped code.                                                                     |

Examples: `references/patterns.md`.

Ships with 4 supporting files:

  • agents/openai.yaml
  • LICENSE
  • references/patterns.md
  • UPSTREAM.md

Mirrored from the author's public source. Install counts from the open skills registry.

The systems behind these skills get built for partners every week.

Partner with us