typescript-refactoring-patterns
Scannednpx machina-cli add skill autohandai/community-skills/typescript-refactoring-patterns --openclawTypeScript Refactoring Patterns
Core Principles
- Type Narrowing Over Type Assertions - Use type guards and discriminated unions instead of
ascasts - Const Assertions for Literals - Use
as constfor immutable literal types - Generic Constraints - Prefer
extendsconstraints overany - Branded Types - Use branded types for domain-specific validation
Refactoring Patterns
Extract Discriminated Union
When you see multiple boolean flags, refactor to discriminated union:
// Before
interface User {
isAdmin: boolean;
isGuest: boolean;
permissions?: string[];
}
// After
type User =
| { role: 'admin'; permissions: string[] }
| { role: 'guest' }
| { role: 'member'; permissions: string[] };
Replace Conditional with Polymorphism
When you see switch statements on type, use the strategy pattern:
// Before
function process(item: Item) {
switch (item.type) {
case 'a': return processA(item);
case 'b': return processB(item);
}
}
// After
const processors: Record<ItemType, (item: Item) => Result> = {
a: processA,
b: processB,
};
const process = (item: Item) => processors[item.type](item);
Extract Type Guard
When narrowing types, create reusable type guards:
function isNonNullable<T>(value: T): value is NonNullable<T> {
return value !== null && value !== undefined;
}
// Usage
const items = array.filter(isNonNullable);
Use Branded Types for Validation
Prevent primitive obsession with branded types:
type UserId = string & { readonly brand: unique symbol };
type Email = string & { readonly brand: unique symbol };
function createUserId(id: string): UserId {
if (!isValidUuid(id)) throw new Error('Invalid user ID');
return id as UserId;
}
Code Smell Detectors
Watch for these patterns and refactor:
anytypes (replace withunknown+ type guards)- Non-null assertions
!(add proper checks) - Type assertions
as(use type guards) - Optional chaining abuse
?.?.?.(restructure data) - Index signatures without validation
Quick Wins
- Enable
strict: truein tsconfig - Use
satisfiesfor type checking without widening - Prefer
readonlyarrays and objects - Use
unknownfor external data, validate at boundaries
Source
git clone https://github.com/autohandai/community-skills/blob/main/typescript-refactoring-patterns/SKILL.mdView on GitHub Overview
This skill introduces expert TypeScript refactoring patterns that boost code cleanliness and type safety. It covers core principles like type narrowing over assertions, const assertions for literals, generic constraints, and branded types, plus practical patterns such as extracting discriminated unions, replacing conditionals with a polymorphic strategy, and creating reusable type guards.
How This Skill Works
The patterns transform code by leveraging TypeScript features: replace scattered boolean flags with a discriminated union, swap type-based switches for a strategy-based dispatch using a processors map, and extract reusable type guards and branded validators. These approaches rely on union types, type predicates, and branded types to enforce stronger safety at compile time.
When to Use It
- When you see multiple boolean flags on a type, refactor to a discriminated union.
- When you have switch statements on a type, replace with a strategy-based dispatch using a processors map.
- When narrowing complex types, extract reusable type guards.
- When you want to avoid primitive obsession, use branded types for domain-specific values such as IDs and emails.
- When validating external data or aiming for deeper type safety, enable strict mode and use satisfies to avoid widening.
Quick Start
- Step 1: Identify boolean-flag patterns and convert to a discriminated union with a role field.
- Step 2: Replace type switches with a processors map and a single process function.
- Step 3: Extract reusable type guards and branded types; enable strict mode and adopt satisfies for checks.
Best Practices
- Prefer type narrowing with guards over as casts.
- Refactor boolean flags into a discriminated union with a role property.
- Create reusable type guards (e.g., isNonNullable) for consistent narrowing.
- Model domain values with branded types to prevent primitive obsession.
- Enable strict mode and use unknown, with validation at boundaries and 'satisfies' for precise typing.
Example Use Cases
- Refactor User: from isAdmin / isGuest to a union type with a role property.
- Use a processors map to replace a type switch in Item processing.
- Filter arrays with a reusable isNonNullable type guard.
- Define branded types like UserId and Email and validate via a factory function.
- Audit for problematic code smells (any, non-null assertions, overuse of as, unsafe chaining) and refactor.