Agent SkillsAmery2010/open-builder › typescript-patterns

typescript-patterns

GitHub

提供 TypeScript 类型安全与可维护性最佳实践,涵盖类型收窄、判别联合、unknown vs any、satisfies 及类型守卫等模式,旨在减少运行时错误并提升代码质量。

public/skills/typescript-patterns/SKILL.md Amery2010/open-builder

Trigger Scenarios

询问 TypeScript 类型系统用法 寻求代码重构建议以提升类型安全 讨论如何避免使用 any 或不当断言

Install

npx skills add Amery2010/open-builder --skill typescript-patterns -g -y
More Options

Non-standard path

npx skills add https://github.com/Amery2010/open-builder/tree/main/public/skills/typescript-patterns -g -y

Use without installing

npx skills use Amery2010/open-builder@typescript-patterns

指定 Agent (Claude Code)

npx skills add Amery2010/open-builder --skill typescript-patterns -a claude-code -g -y

安装 repo 全部 skill

npx skills add Amery2010/open-builder --all -g -y

预览 repo 内 skill

npx skills add Amery2010/open-builder --list

SKILL.md

Frontmatter
{
    "name": "typescript-patterns",
    "tags": [
        "typescript",
        "patterns"
    ],
    "version": "1.0.0",
    "description": "TypeScript patterns and conventions for safer, more maintainable code. Covers narrowing, discriminated unions, satisfies, unknown vs any, type guards, and when to keep types simple."
}

TypeScript Patterns

The goal: types that catch real bugs without making the code harder to read. Lean on inference; reach for advanced types only when they pay rent.

Prefer narrowing over casting

If you find yourself writing as SomeType, ask whether narrowing would work instead. Casts disable the checker; narrowing keeps it on.

function handle(value: string | null) {
  if (value === null) return;
  // value is now `string`
  value.toUpperCase();
}

Cast only when you have more information than the compiler (e.g. parsed JSON whose shape you just validated). Even then, narrow at the boundary — don't sprinkle casts through the call tree.

Discriminated unions

When a type has multiple variants, give each a literal kind field. The compiler narrows on it without effort.

type Result =
  | { kind: "ok"; value: number }
  | { kind: "err"; message: string };

function show(r: Result) {
  if (r.kind === "ok") {
    return r.value;     // narrowed to ok
  }
  return r.message;     // narrowed to err
}

Better than a boolean flag — it scales to N variants and keeps each variant's fields scoped to where they exist.

unknown not any

any opts out of the checker; unknown keeps it on and forces narrowing.

function parse(raw: string): unknown {
  return JSON.parse(raw);
}

const data = parse(input);
if (typeof data === "object" && data !== null && "id" in data) {
  // narrowed; still must check `data.id` shape
}

Use any only when interfacing with an untyped library and there's no realistic alternative. Even then, wrap it in a narrow boundary function with a typed return.

satisfies for literal-typed config

satisfies checks a value against a type without widening it. Useful for configs and registries.

const config = {
  "react-patterns": { version: "1.0.0" },
  "debugging":      { version: "1.0.0" },
} satisfies Record<string, { version: string }>;

// config["react-patterns"] is still keyed to "react-patterns" exactly,
// not widened to string — autocomplete still works on the keys.

Without satisfies, you'd lose the precise key types if you typed : Record<...> directly.

Type guards, not duck checks

If you check the shape in two places, extract a guard.

interface User { id: string; name: string }

function isUser(value: unknown): value is User {
  return (
    typeof value === "object" &&
    value !== null &&
    typeof (value as Record<string, unknown>).id === "string" &&
    typeof (value as Record<string, unknown>).name === "string"
  );
}

For external input (API, postMessage, storage), validate with a schema library (Zod) instead of hand-rolling guards. The library handles the boring cases consistently.

Avoid overly generic generics

Generic types should be motivated. If a function has one type parameter and uses it once, the parameter is probably noise.

Bad:

function getFirst<T>(arr: T[]): T | undefined { return arr[0]; }
function logIt<T>(x: T): void { console.log(x); }   // doesn't need T

The first is fine — the parameter shows up in the return. The second isn't — replace T with unknown.

Don't ship <T extends Record<string, unknown>> machinery just to satisfy a stylistic preference. The simpler signature usually catches the same bugs and is much easier to read.

readonly where it matters

Mark function parameters that aren't mutated as readonly. It documents intent and catches accidental writes.

function sum(nums: readonly number[]): number {
  // nums.push(0); // error — good
  return nums.reduce((a, b) => a + b, 0);
}

For object types, Readonly<T> or per-field readonly works the same way.

Don't type internal-only state aggressively

In a component, useState<string>("") and useState("") produce the same thing — skip the annotation. Annotate when:

  • Initial value is null and you'll set a real value later: useState<User | null>(null).
  • Initial value's literal type isn't what you want: useState<"idle" | "loading" | "done">("idle").
  • Function-prop types where structural matching is ambiguous.

Common bugs the type system catches

  • Forgetting a case in a switch: use never exhaustiveness.
    function area(s: Shape): number {
      switch (s.kind) {
        case "circle": return Math.PI * s.r ** 2;
        case "square": return s.side ** 2;
        default: {
          const _exhaustive: never = s;
          throw new Error(`unhandled: ${_exhaustive}`);
        }
      }
    }
    
  • Off-by-one on optional chaining: obj?.foo.bar will still throw if obj is defined but foo is undefined; you want obj?.foo?.bar.
  • Mixing up || and ??: value ?? 0 falls back only on null/undefined; value || 0 also falls back on "" and 0.

When to write a type vs an interface

For object shapes you'll extend or implement, prefer interface. For unions, tuples, computed types, prefer type. Both are fine for plain object shapes — pick one and be consistent in the file.

Version History

  • fea7528 Current 2026-08-28 16:15

Same Skill Collection

public/skills/accessibility/SKILL.md
public/skills/debugging/SKILL.md
public/skills/react-patterns/SKILL.md
public/skills/tailwind-helpers/SKILL.md

Metadata

Files
0
Version
fea7528
Hash
e79f13b2
Indexed
2026-08-28 16:15

trang chủ - Wiki
Copyright © 2011-2026 iteam. Current version is 2.155.2. UTC+08:00, 2026-09-01 22:09
浙ICP备14020137号-1 $bản đồ khách truy cập$