← Back to list

typescript-patterns
by erikpr1994
⭐ 0🍴 0📅 Jan 22, 2026
SKILL.md
name: typescript-patterns description: "TypeScript idioms, type guards, utility types, and type-safe patterns. Use when working with TypeScript types, generics, or type safety issues."
TypeScript Patterns
Overview
Decision guide for TypeScript patterns focusing on type safety, narrowing, and maintainable type definitions.
Type Narrowing
Discriminated Unions (Preferred)
// DO: Use discriminated unions for state
type Result<T> =
| { status: 'success'; data: T }
| { status: 'error'; error: Error }
| { status: 'loading' };
function handle<T>(result: Result<T>) {
if (result.status === 'success') {
return result.data; // TypeScript knows data exists
}
}
Type Guards
// Custom type guard
function isUser(value: unknown): value is User {
return typeof value === 'object' && value !== null && 'id' in value;
}
// Use assertion functions for validation
function assertUser(value: unknown): asserts value is User {
if (!isUser(value)) throw new Error('Invalid user');
}
Utility Type Patterns
| Pattern | Use Case |
|---|---|
Partial<T> | Optional updates, patch operations |
Required<T> | Ensure all fields present |
Pick<T, K> | Select specific fields |
Omit<T, K> | Exclude fields (API responses) |
Record<K, V> | Type-safe dictionaries |
Extract<T, U> | Filter union types |
Inference Patterns
// Infer return type from function
type ApiResponse = Awaited<ReturnType<typeof fetchUser>>;
// Infer array element type
type Item = (typeof items)[number];
// Const assertion for literal types
const ROLES = ['admin', 'user', 'guest'] as const;
type Role = (typeof ROLES)[number]; // 'admin' | 'user' | 'guest'
Generic Constraints
// Constrain generics meaningfully
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
// Generic with default
type Response<T = unknown> = { data: T; status: number };
Anti-Patterns
Avoid These
// BAD: any defeats type safety
function process(data: any) { ... }
// BAD: Type assertion without validation
const user = response as User;
// BAD: Non-null assertion without guards
user!.profile!.name;
// BAD: Overly complex conditional types
type Complex<T> = T extends A ? B extends C ? D : E : F;
Prefer These
// GOOD: unknown with narrowing
function process(data: unknown) {
if (isValidData(data)) { ... }
}
// GOOD: Validate then use
const user = validateUser(response);
// GOOD: Optional chaining
user?.profile?.name;
// GOOD: Simple, readable types
type Simple<T> = T extends A ? B : C;
Decision Guide
| Situation | Approach |
|---|---|
| Union needs runtime check | Discriminated union with literal discriminant |
| Object might be null/undefined | Optional chaining ?. |
| External data | unknown + type guard |
| Reusing object shape subset | Pick<T, K> or Omit<T, K> |
| String literals as types | as const assertion |
| Complex conditional logic | Break into named types |
Red Flags
- Using
anyanywhere (useunknowninstead) - Multiple
!non-null assertions in a row - Type assertions without preceding validation
@ts-ignoreor@ts-expect-errorwithout explanation- Types that span 10+ lines (decompose them)
Quick Reference
// Branded types for type-safe IDs
type UserId = string & { readonly brand: unique symbol };
function createUserId(id: string): UserId { return id as UserId; }
// Exhaustive switch check
function assertNever(x: never): never {
throw new Error(`Unexpected: ${x}`);
}
// Template literal types
type EventName = `on${Capitalize<'click' | 'focus'>}`; // 'onClick' | 'onFocus'
Score
Total Score
50/100
Based on repository quality metrics
✓SKILL.md
SKILL.mdファイルが含まれている
+20
○LICENSE
ライセンスが設定されている
0/10
○説明文
100文字以上の説明がある
0/10
○人気
GitHub Stars 100以上
0/15
○最近の活動
3ヶ月以内に更新がある
0/10
○フォーク
10回以上フォークされている
0/5
✓Issue管理
オープンIssueが50未満
+5
✓言語
プログラミング言語が設定されている
+5
○タグ
1つ以上のタグが設定されている
0/5
Reviews
💬
Reviews coming soon