typescript-satisfies-operator
Guides proper usage of TypeScript's satisfies operator vs type annotations. Use this skill when deciding between type annotations (colon) and satisfies, validating object shapes while preserving literal types, or troubleshooting type inference issues.
Best use case
typescript-satisfies-operator is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
Guides proper usage of TypeScript's satisfies operator vs type annotations. Use this skill when deciding between type annotations (colon) and satisfies, validating object shapes while preserving literal types, or troubleshooting type inference issues.
Teams using typescript-satisfies-operator should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/typescript-satisfies-operator/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How typescript-satisfies-operator Compares
| Feature / Agent | typescript-satisfies-operator | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
Guides proper usage of TypeScript's satisfies operator vs type annotations. Use this skill when deciding between type annotations (colon) and satisfies, validating object shapes while preserving literal types, or troubleshooting type inference issues.
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
Related Guides
SKILL.md Source
# TypeScript: The `satisfies` Operator
## Core Concept
The `satisfies` operator validates that an expression matches a type **without changing the inferred type**. This is different from type annotations (`:`) which widen the type.
**Key insight from Matt Pocock:**
- "When you use a colon, the type BEATS the value"
- "When you use `satisfies`, the value BEATS the type"
## Type Annotation vs Satisfies
```typescript
type RoutingPathname = "/products" | "/cart" | "/checkout";
// Type annotation - widens to union
const url1: RoutingPathname = "/products";
// url1 is typed as: RoutingPathname (wide)
// Satisfies - keeps literal
const url2 = "/products" satisfies RoutingPathname;
// url2 is typed as: '/products' (narrow)
// Why it matters:
const test1: "/products" = url1; // Error: RoutingPathname not assignable to '/products'
const test2: "/products" = url2; // Works
```
## Classic Use Case: Object Validation with Preserved Types
```typescript
type Colors = "red" | "green" | "blue";
type RGB = [red: number, green: number, blue: number];
// Type annotation loses specific property types
const palette1: Record<Colors, string | RGB> = {
red: [255, 0, 0],
green: "#00ff00",
blue: [0, 0, 255],
};
palette1.green.toUpperCase(); // Error: 'toUpperCase' doesn't exist on string | RGB
// Satisfies validates AND preserves literal types
const palette2 = {
red: [255, 0, 0],
green: "#00ff00",
bleu: [0, 0, 255], // Error: Typo caught!
} satisfies Record<Colors, string | RGB>;
palette2.green.toUpperCase(); // Works - green is inferred as string
```
## When to Use What
| Annotation Style | Type vs Value | Use Case |
| ---------------- | ------------- | ---------------------------------- |
| `: Type` (colon) | Type wins | Need wider type for reassignment |
| `satisfies Type` | Value wins | Need validation + narrow inference |
| `as Type` | Lies to TS | Escape hatch (use sparingly!) |
| No annotation | Inference | Most common - let TS infer |
## Rule of Thumb
**Use `satisfies` when:**
1. You want the EXACT type of the variable, not the wider type
2. The type is complex enough that you want validation you didn't mess it up
**Use colon annotation when:**
1. You need to reassign the variable later with different values of the union
2. You explicitly want the wider type
## Common Pattern: `as const satisfies`
Combine `as const` for immutability with `satisfies` for validation:
```typescript
const routes = {
home: "/",
products: "/products",
cart: "/cart",
} as const satisfies Record<string, string>;
// routes.home is typed as '/' (readonly literal)
// But validated against Record<string, string>
```
### Prefer `as const satisfies` Over Type Annotation
When you need both validation AND literal type preservation:
```typescript
// Bad - type annotation widens types, loses literals
const LANG_MAP: Record<string, string> = {
en: '1',
cs: '2',
} as const;
// LANG_MAP.en is just string, not '1'
// Good - satisfies validates while preserving literal types
const LANG_MAP = {
en: '1',
cs: '2',
} as const satisfies Record<string, string>;
// LANG_MAP.en is '1' (narrow literal type)
```
### Real-World Example: Config Validation
```typescript
type Locale = 'en' | 'cs';
// Validates all locales are present, preserves specific values
const SHOP_GRAPHQL_LOCALE_LANGUAGE_ID_MAP = {
en: '1',
cs: '2',
} as const satisfies Record<Locale, string>;
// TypeScript will error if you miss a locale:
const INCOMPLETE_MAP = {
en: '1',
// cs: '2', // Error: Property 'cs' is missing
} as const satisfies Record<Locale, string>;
```
## Real-World Examples
### Configuration Objects
```typescript
type Config = {
api: string;
timeout: number;
retries: number;
};
// Validates shape, but keeps literal types for autocomplete
const config = {
api: "https://api.example.com",
timeout: 5000,
retries: 3,
} satisfies Config;
// config.api is 'https://api.example.com', not string
```
### Event Handlers Map
```typescript
type EventMap = Record<string, (...args: unknown[]) => void>;
const handlers = {
click: (x: number, y: number) => console.log(x, y),
submit: (data: FormData) => console.log(data),
} satisfies EventMap;
// handlers.click is (x: number, y: number) => void
// Not (...args: unknown[]) => void
```
### Exhaustive Checks with Records
```typescript
type Status = "pending" | "approved" | "rejected";
const statusLabels = {
pending: "Waiting for review",
approved: "Approved",
rejected: "Rejected",
} satisfies Record<Status, string>;
// If you add a new Status, TypeScript will error until you add it here
```
## References
- [TypeScript 4.9 Release Notes](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-9.html#the-satisfies-operator)
- [Matt Pocock - Clarifying the satisfies Operator](https://www.totaltypescript.com/clarifying-the-satisfies-operator)
- [GitHub Issue #47920 - Original Proposal](https://github.com/microsoft/TypeScript/issues/47920)Related Skills
typescript-interface-vs-type
Guides when to use interface vs type in TypeScript. Use this skill when defining object types, extending types, or choosing between interface and type aliases.
typescript-best-practices
Guides TypeScript best practices for type safety, code organization, and maintainability. Use this skill when configuring TypeScript projects, deciding on typing strategies, writing async code, or reviewing TypeScript code quality.
typescript-advanced-types
Master TypeScript's advanced type system including generics, conditional types, mapped types, template literals, and utility types for building type-safe applications. Use when implementing complex type logic, creating reusable type utilities, or ensuring compile-time type safety in TypeScript projects.
what-not-to-do-as-product-manager
Anti-patterns and mistakes to avoid as a product manager. Use when evaluating leadership behaviors, improving team dynamics, reflecting on management practices, or onboarding new product managers.
visual-cues-cta-psychology
Design effective CTAs using visual attention and gaze psychology principles. Use when designing landing pages, button hierarchies, conversion elements, or optimizing user attention flow through interfaces.
vercel-sandbox
Run agent-browser + Chrome inside Vercel Sandbox microVMs for browser automation from any Vercel-deployed app. Use when the user needs browser automation in a Vercel app (Next.js, SvelteKit, Nuxt, Remix, Astro, etc.), wants to run headless Chrome without binary size limits, needs persistent browser sessions across commands, or wants ephemeral isolated browser environments. Triggers include "Vercel Sandbox browser", "microVM Chrome", "agent-browser in sandbox", "browser automation on Vercel", or any task requiring Chrome in a Vercel Sandbox.
value-realization
Analyze if end users discover clear value. Use when evaluating product concepts, analyzing adoption, or uncertain about direction.
user-story-fundamentals
Capture requirements from user perspective with structured user stories. Use when writing backlog items, defining acceptance criteria, prioritizing features, or communicating requirements between product and development.
trust-psychology
Build trust signals that reduce perceived risk and enable user action. Use when designing landing pages, checkout flows, onboarding experiences, or any conversion point where user hesitation is a barrier.
theme-epic-story
Structure product work hierarchically using themes, epics, and stories. Use when organizing backlogs, planning releases, communicating with stakeholders, or breaking down large initiatives into manageable work.
tailwind-v4-configuration
Configure Tailwind CSS v4 with CSS-first approach. Use when installing, migrating from v3, setting up build tools (Vite/PostCSS/CLI), customizing themes with @theme, or configuring plugins.
status-quo-bias
Understand and design for users' preference for current state over change. Use when planning migrations, introducing new features, designing defaults, or overcoming resistance to product adoption.