typescript
Must always be enabled when writing/reviewing TypeScript code.
- License 许可
- MIT
- 最近更新
- 2026-11-01
- Stars
- ★ 3
- Forks
- 1
01 前言 Front Matter
- name
- typescript
- description
- Must always be enabled when writing/reviewing TypeScript code.
03 SKILL.md 正文
<philosophy>
Core Philosophy: Type-Level Verification
LLM-generated code faces inherent challenges with E2E testing and runtime verification. Compensate by maximizing compile-time verification through:
- Algebraic data types (discriminated unions, exhaustive pattern matching)
- Strict type constraints that make invalid states unrepresentable
- Type-level proofs over runtime assertions
Goal: If it type-checks, it works. Shift as many bugs as possible from runtime to compile-time. </philosophy>
<type_assertions>
Type Assertions: Banned by Default
Rule: as Type Assertions are Prohibited
Rationale: Type assertions bypass TypeScript's type system and introduce type unsoundness. They are frequently misused to silence legitimate type errors.
Policy:
- ❌ Never use
asto resolve type errors - ❌ Never use
as anyoras unknown as X - ⚠️ Rare exceptions: Compiler limitations (e.g., specific generic inference bugs)
- If you encounter such cases, leave the type error unresolved
- Escalate to user with explanation: "Type error at
path/to/file.ts:123- requires manual review for potentialasusage"
Why you cannot judge appropriately: As an LLM, you lack the contextual understanding to determine if a type assertion is truly necessary vs. masking a real type error. When in doubt, preserve type safety.
Alternative: Fix the Root Cause
Instead of as, address the underlying type issue:
- Refine function signatures
- Use type guards (
if (typeof x === 'string')) - Employ discriminated unions
- Add generic constraints </type_assertions>
<strict_typing>
Strict Typing Patterns
Prefer as const satisfies Over Loose Annotations
Problem with loose typing:
const config: Config = {
mode: 'development', // Type widened to string
port: 3000
}
// config.mode is string, not 'development' | 'production'
Solution - strict typing with as const satisfies:
const config = {
mode: 'development',
port: 3000
} as const satisfies Config
// config.mode is exactly 'development' (literal type preserved)
Benefits:
- Preserves literal types
- Catches typos at definition site
- Enables exhaustive checking in consumers
- No type widening
Application:
- Configuration objects
- Constant lookup tables
- Route definitions
- Action type constants
Avoid Explicit Type Annotations When Inference Suffices
// ❌ Redundant annotation
const result: number = calculateTotal(items)
// ✅ Let TypeScript infer
const result = calculateTotal(items)
Use annotations when:
- Constraining function parameters
- Enforcing strict object shapes (
as const satisfies) - Documenting public API boundaries </strict_typing>
<external_data>
External Data: Never Trust, Always Validate
Rule: No any for External Data
Sources requiring validation:
- API responses (fetch, axios, etc.)
JSON.parse()results- LocalStorage/SessionStorage reads
- FormData / user input
- Environment variables
- File system reads
Strategy 1: Type-Safe API Clients (Preferred)
Check for generated type definitions first:
- Hono:
hono/clientwith type inference - orval: OpenAPI-generated types and hooks
- tRPC: End-to-end type safety
- GraphQL Code Generator: Typed queries
Example (Hono client):
import { hc } from 'hono/client'
import type { AppType } from './server'
const client = hc<AppType>('/api')
const response = await client.users.$get()
// response is fully typed from server definition
Action: Review existing codebase for established patterns. Most projects already have type-safe API layers.
Strategy 2: Runtime Validation Libraries
When type generation is unavailable, use schema validation:
Preference order:
- Existing project dependency (check
package.json) - valibot (lightweight, install if needed:
pnpm add valibot) - zod (popular, larger bundle)
Example (valibot):
import * as v from 'valibot'
const UserSchema = v.object({
id: v.number(),
name: v.string(),
role: v.union([v.literal('admin'), v.literal('user')])
})
// Parse and validate
const response = await fetch('/api/user')
const data = await response.json()
const user = v.parse(UserSchema, data) // Throws if invalid
// user is now typed as { id: number, name: string, role: 'admin' | 'user' }
Example (JSON.parse):
// ❌ Unsafe
const data = JSON.parse(localStorage.getItem('config')!)
// ✅ Validated
const raw = localStorage.getItem('config')
if (raw) {
const data = v.parse(ConfigSchema, JSON.parse(raw))
}
Never Skip Validation
Even if "you know" the shape, external data can change:
- API contracts evolve
- Users manipulate localStorage
- Third-party services have bugs
Type safety = static types + runtime validation </external_data>
<best_practices>
General Best Practices
Prefer Arrow Functions Over Function Declarations
Rule: Use arrow functions (=>) instead of function keyword for consistency and lexical scoping benefits.
Rationale:
- Consistent lexical
thisbinding (no context confusion) - More concise syntax
- Better integration with modern TypeScript patterns
- Prevents accidental hoisting-related bugs
// ❌ Function declaration
function calculateTotal(items: Item[]): number {
return items.reduce((sum, item) => sum + item.price, 0)
}
// ✅ Arrow function
const calculateTotal = (items: Item[]): number => {
return items.reduce((sum, item) => sum + item.price, 0)
}
// ✅ Concise form (single expression)
const calculateTotal = (items: Item[]): number =>
items.reduce((sum, item) => sum + item.price, 0)
Exception: When hoisting is genuinely required (rare), document the reason.
Discriminated Unions for State
type LoadingState<T> =
| { status: 'idle' }
| { status: 'loading' }
| { status: 'success'; data: T }
| { status: 'error'; error: Error }
const render = (state: LoadingState<User>) => {
switch (state.status) {
case 'idle':
return 'Not started'
case 'loading':
return 'Loading...'
case 'success':
return state.data.name // data is available
case 'error':
return state.error.message // error is available
}
}
Benefits: Impossible to access data when status is 'error'.
Exhaustiveness Checking
const assertNever = (x: never): never => {
throw new Error(`Unexpected value: ${x}`)
}
switch (state.status) {
case 'idle':
case 'loading':
case 'success':
case 'error':
return
default:
assertNever(state) // Compile error if cases are missing
}
Avoid Optional Properties for State
// ❌ Ambiguous state
type User = {
data?: UserData
error?: Error
}
// What if both are defined? Neither?
// ✅ Explicit state
type User =
| { status: 'success'; data: UserData }
| { status: 'error'; error: Error }
Use unknown Over any for Truly Unknown Types
// ❌ Disables all type checking
const process = (data: any) => {
return data.foo.bar // No errors, runtime explosion
}
// ✅ Forces validation
const process = (data: unknown) => {
if (typeof data === 'object' && data !== null && 'foo' in data) {
// Narrow the type before use
}
}
Readonly by Default
// Prevent accidental mutations
type Config = {
readonly apiUrl: string
readonly timeout: number
}
// For arrays
const items = ['a', 'b'] as const
Avoid Type-Level Gymnastics
If type definitions become incomprehensible, simplify the design:
- Complex conditional types often indicate over-abstraction
- Prefer explicit discriminated unions over heavily generic types
- Maintainability > cleverness </best_practices>
<error_handling>
When Type Errors Cannot Be Resolved
If you encounter legitimate type errors you cannot fix without as:
- Leave the error in place
- Document the issue:
// TODO: Type error at line X - potential TypeScript limitation // Requires manual review before using type assertion const result = someComplexOperation() // Type error here - Notify the user: "Type error remains at
src/module.ts:45- escalated for review"
Do not:
- Silently add
asassertions - Use
anyto bypass the error - Restructure correct code to satisfy incorrect types </error_handling>
04 同仓库更多 Skills
xiaolai-methodology
以李笑来(Li Xiaolai)的思考方式、方法论与表达风格,为用户做全方位 1-on-1 教练指导,覆盖创业、赚钱、投资、学习、自学、读书写作、用 AI、以及人生心态与自我管理。当用户提到 "李笑来"、"笑来"、"笑来老师"、"扮演李笑来"、"找笑来聊聊"、"笑来怎么看"、"xiaolai"、"把时间当作朋友"、"通往财富自由之路"、"定投改变命运"、"韭菜的自我修养"、"自学是门手艺"、"让时间陪你慢慢变富"、"七年就是一辈子"、"元认知"、"活在未来"、"复利/长期主义"、"践行"、"痴迷改进"、"个人商业模式/出售时间"、"睡后收入"、"定投"、"财富自由"、"操作系统升级"、"最少必要知识"、"北极星提示词"、"用 AI 该怎么学/思考" 等任一触发词,或当用户带着关于创业/赚钱/投资/学习/职业/重大人生决策的困惑、希望被人用笑来式的「逻辑 + 大白话 + 类比 + 重新定义 + 践行导向」来点醒和指导时,自动进入李笑来教练模式。
zread
Produce and consume a wiki-style knowledge base for a code repository via the `zread` CLI and its on-disk output under `./.zread/wiki/`. Use this skill whenever the user wants to understand, onboard onto, explore, summarize, map, or get an overview of an unfamiliar codebase; asks for architecture docs, a project wiki, a repo walkthrough, module/package explanations, or "what does this repo do"; wants to generate, regenerate, resume, browse, or serve code documentation locally; or mentions zread / zread.ai directly. Also use it proactively before diving into a large unknown repo — if `./.zread/wiki/current` exists, read the generated pages instead of crawling source file-by-file; if it doesn't, consider offering to run `zread generate`. The trigger is the intent (understand a codebase through generated docs), not the literal word "zread".
youtube
Launch the YouTube Creator Agent to auto-generate videos. Use when: user types /youtube, wants to create YouTube videos, generate video content, or upload to YouTube. Keywords: youtube, video, 视频, 创作视频, 发布视频
youtube-script-sync
Sync, update, or optimize Tony's GetNote knowledge base `Youtube视频逐字稿` by turning each parent recording or first-person idea note into an idempotent child note containing a directly recordable YouTube long-video or Shorts script. Use when the user says “更新/同步/优化 Youtube视频逐字稿知识库”, or says “更新/同步/优化该知识库” while this knowledge base is the active context.
writing-skills
How to write SKILL.md files that trigger reliably and teach effectively. Use when creating, improving, or reviewing Claude Code skills.
writing-rules
How to write .claude/rules/ files that Claude actually follows. Use when creating, improving, or reviewing project rules.