typescript-advanced
Advanced TypeScript type system patterns for generics, conditional types, mapped types, template literals, and utility types. Use when implementing complex type logic, creating reusable type utilities, or enforcing type safety beyond basic annotations — discriminated unions with exhaustive checks, branded/opaque types for domain safety, satisfies vs as const decisions, NoInfer for inference control, module augmentation for third-party types, or choosing between hand-rolled types and type-fest utilities. Do not use for basic TypeScript syntax or simple type annotations.
What this skill does
# TypeScript Advanced: Patterns, Pitfalls & type-fest
This skill defines the rules, conventions, and architectural decisions for writing advanced
TypeScript. It is intentionally opinionated to prevent common type-level bugs and enforce
patterns that produce safe, maintainable code.
For detailed API documentation of TypeScript features, use other appropriate tools
(documentation lookup, web search, etc.) — this skill focuses on **when**, **why**, and
**how** to use advanced type features correctly.
## Type Safety Philosophy
### `any` vs `unknown` vs `never` — the only rule you need
| Type | Assignable from | Assignable to | Operations | Use for |
| --------- | --------------- | ---------------------- | -------------- | ----------------------------------- |
| `any` | anything | anything | all (UNSAFE) | Never in new code |
| `unknown` | anything | only `unknown` / `any` | none unnarowed | External inputs, JSON, user data |
| `never` | nothing | anything | none | Exhaustive checks, unreachable code |
**Rule: never use `any` in new code.** Use `unknown` for external boundaries and narrow
before operating. Use `never` for exhaustiveness and impossible states.
### Prefer unions over enums
```typescript
// Avoid — numeric enums are structurally assignable to number (footgun)
enum Direction {
Up,
Down,
Left,
Right,
}
function go(d: Direction) {}
go(42); // no error — TypeScript allows any number!
// Prefer — exhaustive, tree-shakeable, no runtime artifact
type Direction = "up" | "down" | "left" | "right";
```
String enums are safer than numeric but still carry runtime overhead and import friction.
String literal unions are the default choice unless you need reverse mapping.
### `interface` vs `type` — decision table
| Scenario | Use | Why |
| ----------------------------------------- | ----------- | ------------------------------------------------- |
| Object shapes, class contracts | `interface` | Declaration merging, better error messages |
| Unions, intersections, mapped/conditional | `type` | Only `type` supports these |
| Third-party augmentation needed | `interface` | Only interfaces support declaration merging |
| Public API types (libraries) | `interface` | Consumers can augment; better display in tooltips |
| Internal computed types | `type` | More expressive, no accidental merging |
---
## Discriminated Unions & Exhaustive Checks
### The `never` exhaustiveness pattern
Every `switch` / `if-else` chain on a discriminated union must handle all variants.
Use the `never` assignment to get a compile-time error when a new variant is added:
```typescript
type Result<T> =
| { status: "ok"; data: T }
| { status: "error"; error: Error }
| { status: "loading" };
function handle<T>(result: Result<T>): string {
switch (result.status) {
case "ok":
return JSON.stringify(result.data);
case "error":
return result.error.message;
case "loading":
return "Loading...";
default:
const _exhaustive: never = result;
return _exhaustive; // compile error if a variant is unhandled
}
}
```
### Rules for discriminated unions
- **Discriminant must be a literal type** — `string`, `number`, `boolean` literals. Wide
types like `string` do not narrow.
- **Keep the discriminant property name consistent** across all members (`kind`, `type`,
`status`).
- **Avoid optional discriminants** — `status?: "ok" | "error"` breaks narrowing.
---
## Branded Types — Nominal Safety in a Structural System
TypeScript is structural: `UserId` (a `string`) and `OrderId` (a `string`) are
interchangeable by default. Branded types break this at the type level with zero
runtime overhead.
### Recommended pattern: `unique symbol` brand
```typescript
declare const __brand: unique symbol;
type Brand<T, B> = T & { readonly [__brand]: B };
type UserId = Brand<string, "UserId">;
type OrderId = Brand<string, "OrderId">;
// Constructor = the single trust boundary, validate here
const toUserId = (id: string): UserId => id as UserId;
const toOrderId = (id: string): OrderId => id as OrderId;
function getUser(id: UserId) {
/* ... */
}
getUser(toUserId("abc")); // ok
getUser(toOrderId("abc")); // ERROR — OrderId not assignable to UserId
getUser("abc"); // ERROR — string not assignable to UserId
```
### When to use branded types
- **IDs** — `UserId`, `OrderId`, `ProductId` prevent cross-assignment
- **Units** — `Meters`, `Feet`, `USD`, `EUR` prevent arithmetic mistakes
- **Validated strings** — `Email`, `URL`, `Slug` encode that validation has happened
- **Opaque tokens** — `JWTToken`, `APIKey` prevent accidental logging/display
### type-fest alternative
Use `Tagged<T, Tag>` from type-fest for multi-tag composition and metadata:
```typescript
import type { Tagged, GetTagMetadata } from "type-fest";
type UserId = Tagged<string, "UserId">;
type AdminId = Tagged<UserId, "Admin">; // composable — both tags preserved
```
---
## Modern Inference Tools
### `satisfies` — validate without widening (TS 4.9+)
```typescript
type Theme = Record<"primary" | "secondary", string | string[]>;
// Type annotation: loses specific types
const t1: Theme = { primary: "#000", secondary: ["#111", "#222"] };
t1.secondary.map((s) => s); // ERROR — string | string[] has no .map
// satisfies: validates structure, keeps specific inference
const t2 = { primary: "#000", secondary: ["#111", "#222"] } satisfies Theme;
t2.secondary.map((s) => s); // ok — inferred as string[]
```
**Use `satisfies` when:** you want config validation (catch typos in keys) but also need
autocomplete on specific values.
### `const` type parameters — generic literal inference (TS 5.0+)
```typescript
// Without const: T = string[]
function routes<T extends string[]>(r: T): T {
return r;
}
// With const: T = readonly ["users", "posts"]
function routes<const T extends string[]>(r: T): T {
return r;
}
const r = routes(["users", "posts"]); // readonly ["users", "posts"]
```
**Use `const` type parameters when:** building registries, config factories, or any
generic where preserving literal types at the call site matters.
### `NoInfer<T>` — control inference sources (TS 5.4+)
Prevents a parameter from contributing to type inference — it reads `T` but doesn't
influence what `T` becomes:
```typescript
function createFSM<const TState extends string>(config: {
states: TState[];
initial: NoInfer<TState>; // must be from states, can't introduce new values
}) {
/* ... */
}
createFSM({ states: ["idle", "running"], initial: "idle" }); // ok
createFSM({ states: ["idle", "running"], initial: "stopped" }); // ERROR
```
**Use `NoInfer` when:** a function has multiple parameters sharing a type param, and one
should be constrained to what the others infer — not contribute new candidates.
---
## type-fest: Don't Reinvent the Wheel
[type-fest](https://github.com/sindresorhus/type-fest) provides 200+ utility types with
zero runtime cost (types-only). Always check type-fest before writing a custom utility.
```typescript
import type { Simplify, Merge, SetRequired, LiteralUnion } from "type-fest";
```
### Decision table: built-in vs type-fest
| Need | Built-in | type-fest |
| ----------------------------------------- | --------------------- | ---------------------------------------- |
| Make keys optional/required | `Partial`, `Required` | `SetOptional`, `SetRequired` (per-key) |
| Deep partial/readonly | — | `PartialDeep`, `ReadonlyDeep` |
| Merge two types (override, not intersect) | — Related in General
modeling-omnistudio-epc-catalog
IncludedSalesforce Industries CME EPC product-modeling skill for Product2-based catalog creation. Use when creating EPC products, configuring product attributes, building offer bundles with Product Child Items, or reviewing EPC DataPack JSON metadata for product catalog changes. TRIGGER when: user creates or updates Product2 EPC records, AttributeAssignment payloads, AttributeMetadata/AttributeDefaultValues, Offer bundles, or ProductChildItem relationships. DO NOT TRIGGER when: designing OmniScripts/FlexCards/Integration Procedures (use building-omnistudio-omniscript, building-omnistudio-flexcard, or building-omnistudio-integration-procedure), implementing Apex business logic (use generating-apex), or troubleshooting deployment pipelines (use deploying-metadata).
relationship-science-coach
IncludedUse this skill for direct, practical adult relationship coaching: couples conflict, repair, trust, marriage, dating, flirting, attachment patterns, emotional connection, sex, desire differences, eroticism, kink negotiation, affection, love languages, breakups, and long-term passion. Draw on Gottman, EFT and Hold Me Tight, attachment science, modern sex research, Perel, Nagoski, Kerner, Schnarch, Love and Stosny, and flexible love-language tools. Be concrete and low-hedge. Redirect only for imminent danger, abuse, coercive control, minors, non-consent, self-harm, stalking, or medical/legal/psychiatric decisions.
building-sf-integrations
IncludedSalesforce integration architecture and runtime plumbing with 120-point scoring. Use this skill to set up Named Credentials, External Credentials, External Services, REST/SOAP callout patterns, Platform Events, and Change Data Capture. TRIGGER when: user sets up Named Credentials, External Services, REST/SOAP callouts, Platform Events, CDC, or touches .namedCredential-meta.xml files. DO NOT TRIGGER when: Connected App/OAuth config (use configuring-connected-apps), Apex-only logic (use generating-apex), or data import/export (use handling-sf-data).
venue-templates
IncludedAccess comprehensive LaTeX templates, formatting requirements, and submission guidelines for major scientific publication venues (Nature, Science, PLOS, IEEE, ACM), academic conferences (NeurIPS, ICML, CVPR, CHI), research posters, and grant proposals (NSF, NIH, DOE, DARPA). This skill should be used when preparing manuscripts for journal submission, conference papers, research posters, or grant proposals and need venue-specific formatting requirements and templates.
let-fate-decide
IncludedDraws the 12 Houses of the Zodiac Tarot spread to inject entropy into planning when prompts are vague, ambiguous, or casually delegated. Interprets the spread to guide next steps. Use when the user says 'let fate decide', 'YOLO', 'whatever', 'idk', or other nonchalant phrases, makes Yu-Gi-Oh references, or when you are about to arbitrarily pick between multiple reasonable approaches. Prefer over ask-questions-if-underspecified when the user's tone is casual or playful rather than precision-seeking.
net-ops
IncludedCross-platform network troubleshooting (Windows, macOS, Linux) via local or remote shell. Use for: DNS broken, can't resolve hostnames, nslookup/dig works but apps fail, NRPT, WFP, scutil, /etc/resolver, systemd-resolved, /etc/resolv.conf, NetworkManager, VPN DNS leak residue (ProtonVPN/Mullvad/WireGuard/AnyConnect), AV/firewall blocking DNS or DoH, Tailscale DNS interaction, intermittent connectivity, remote diagnostics over SSH.