Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Use this skill when working in a strict TypeScript codebase and you want reliable patterns for modeling data, narrowing, generics, and configuration—without papering over errors with `as` casts.
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-05 | ✗→✓ | ▲ Improved | -13% | 0% |
| case-01 | ✓→✓ | = Same ✓ | 85% | 0% |
| case-02 | ✓→✓ | = Same ✓ | 121% | 0% |
| case-03 | ✓→✓ | = Same ✓ | 112% | 0% |
| case-04 | ✓→✓ | = Same ✓ | 57% | 0% |
Use this skill when working in a strict TypeScript codebase and you want reliable patterns for modeling data, narrowing, generics, and configuration—without papering over errors with as casts.
Prefer strict: true and keep these on unless you have a strong reason:
noUncheckedIndexedAccessexactOptionalPropertyTypesnoImplicitOverride (when using classes)Treat any relaxation as a conscious decision with a documented reason.
unknown over anyUse unknown at boundaries:
Then narrow:
tsfunction isRecord(x: unknown): x is Record<string, unknown> { return typeof x === "object" && x !== null; }
Use the built-in primitives:
typeof for primitivesinstanceof for classes/errors"in" for structural checkststype Result = | { ok: true; value: string } | { ok: false; error: string }; function handle(r: Result) { if (!r.ok) return r.error; return r.value; }
Avoid boolean soup (isLoading, hasError, isEmpty …). Prefer:
tstype LoadState = | { kind: "idle" } | { kind: "loading" } | { kind: "error"; message: string } | { kind: "ready"; items: string[] };
Useful:
Pick, Omit for DTO shapingReturnType, Parameters for typed wrappersPartial only for “patch” objects (not for real domain models)Anti-pattern:
Partial<Model> to “get around” missing required fieldssatisfies > type assertionsPrefer:
tsconst routes = { home: "/", settings: "/settings", } satisfies Record<string, string>;
Avoid:
as Record<string, string> (it can lie)as const for literal preservationUse as const to keep literals and readonly tuples:
tsconst roles = ["admin", "user"] as const; type Role = (typeof roles)[number];
Add generics when:
Stop adding generics when:
Good generic:
tsexport function groupBy<T, K extends string | number>( items: T[], key: (item: T) => K, ): Record<K, T[]> { return items.reduce((acc, item) => { const k = key(item); (acc[k] ??= []).push(item); return acc; }, {} as Record<K, T[]>); }
as casts (make invalid states unrepresentable)Prefer:
never to enforce exhaustivenesstsfunction assertNever(x: never): never { throw new Error("Unexpected: " + String(x)); }
Recommended baseline:
strict: trueskipLibCheck: true (usually OK)noEmit: true in typecheck scriptsmoduleResolution aligned with your runtime/bundlerIf using ESM:
grep/glob to find any/unknown boundaries, and track how a value flows through modules.any for convenience instead of writing a small guardPartial<T> as a general-purpose escape hatchas casts instead of validating onceunknown → narrowed)switch/if for key unionsOther measured skills in the registry, with their headline benchmark lift.