Skip to main content

typescript-fieldguide

This skill should be used when writing TypeScript, eliminating any types, implementing Zod validation, or when strict type safety is needed. Covers modern TS 5.5+ features and runtime validation patterns.

소스 정보

저장소
outfitter-dev/outfitter
최근 소스 활동
2026년 2월 24일 23:09
감지된 SKILL.md 언어
영어
스타
6
포크
1

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

파일 탐색기
16 개 파일

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
typescript-fieldguide
description
This skill should be used when writing TypeScript, eliminating any types, implementing Zod validation, or when strict type safety is needed. Covers modern TS 5.5+ features and runtime validation patterns.
metadata
{"version":"1.0.1"}
# TypeScript Development Type-safe code = compile-time errors = runtime confidence. <when_to_use> - Writing new TypeScript code - Eliminating `any` types - Using modern TypeScript 5.5+ features - Validating API inputs/outputs with Zod - Implementing Result types and discriminated unions - Creating branded types for domain concepts NOT for: runtime-only logic unrelated to types, non-TypeScript projects </when_to_use> <config> **tsconfig.json** strict settings: ```json { "compilerOptions": { "strict": true, "noUncheckedIndexedAccess": true, "exactOptionalPropertyTypes": true, "noImplicitOverride": true, "noPropertyAccessFromIndexSignature": true, "noFallthroughCasesInSwitch": true, "noImplicitReturns": true, "forceConsistentCasingInFileNames": true, "verbatimModuleSyntax": true, "isolatedModules": true, "skipLibCheck": false } } ``` **Version requirements**: TS 5.2+ (`using`), TS 5.4+ (`NoInfer`), TS 5.5+ (inferred predicates) </config> ## Core Patterns <eliminating_any> `any` defeats the type system. Use `unknown` + guards. ```typescript // ❌ NEVER function process(data: any) { return data.value; } // ✅ ALWAYS function process(data: unknown): string { if (!hasValue(data)) throw new TypeError("Invalid"); return data.value.toString(); } function hasValue(v: unknown): v is { value: unknown } { return typeof v === "object" && v !== null && "value" in v; } ``` Validate at boundaries: ```typescript async function fetchUser(id: string): Promise<User> { const data: unknown = await fetch(`/api/users/${id}`).then((r) => r.json()); return UserSchema.parse(data); } ``` </eliminating_any> <result_types> Exceptions hide errors from types. Result makes them explicit. ```typescript type Result<T, E = Error> = | { readonly ok: true; readonly value: T } | { readonly ok: false; readonly error: E }; type UserError = | { readonly type: "not-found"; readonly id: string } | { readonly type: "network"; readonly message: string }; async function getUser(id: string): Promise<Result<User, UserError>> { try { const response = await fetch(`/api/users/${id}`); if (response.status === 404) return { ok: false, error: { type: "not-found", id } }; if (!response.ok) return { ok: false, error: { type: "network", message: response.statusText }, }; return { ok: true, value: await response.json() }; } catch (e) { return { ok: false, error: { type: "network", message: String(e) } }; } } // Caller must handle const result = await getUser(id); if (!result.ok) { switch (result.error.type) { case "not-found": return showNotFound(result.error.id); case "network": return showError(result.error.message); } } return renderUser(result.value); ``` See [result-pattern.md](references/result-pattern.md) for utilities (`map`, `flatMap`, `combine`). </result_types> <discriminated_unions> Prevent illegal state combinations. ```typescript // ❌ Allows { status: 'loading', data: user, error: 'Failed' } type Request = { status: 'idle'|'loading'|'success'|'error'; data?: User; error?: string; }; // ✅ Only valid states type RequestState = | { readonly status: 'idle' } | { readonly status: 'loading' } | { readonly status: 'success'; readonly data: User } | { readonly status: 'error'; readonly error: string }; function render(state: RequestState): JSX.Element { switch (state.status) { case 'idle': return <div>Ready</div>; case 'loading': return <div>Loading...</div>; case 'success': return <div>{state.data.name}</div>; case 'error': return <div>Error: {state.error}</div>; default: return assertNever(state); } } function assertNever(value: never): never { throw new Error(`Unhandled: ${JSON.stringify(value)}`); } ``` </discriminated_unions> <branded_types> Prevent mixing incompatible primitives. ```typescript declare const __brand: unique symbol; type Brand<T, B extends string> = T & { readonly [__brand]: B }; type UserId = Brand<string, "UserId">; type ProductId = Brand<string, "ProductId">; function createUserId(value: string): UserId { if (!/^user-\d+$/.test(value)) throw new TypeError(`Invalid: ${value}`); return value as UserId; } const userId = createUserId("user-123"); // getUser(productId); // ❌ Type error getUser(userId); // ✅ Works ``` Security: ```typescript type SanitizedHtml = Brand<string, "SanitizedHtml">; function sanitize(raw: string): SanitizedHtml { return escapeHtml(raw) as SanitizedHtml; } function render(html: SanitizedHtml): void { element.innerHTML = html; // Type proves sanitization } ``` See [branded-types.md](references/branded-types.md) for advanced patterns. </branded_types> ## Modern TypeScript (5.2+) <resource_management> `using` for automatic cleanup (TS 5.2+): ```typescript class DatabaseConnection implements Disposable { [Symbol.dispose]() { this.close(); } } function query() { using conn = new DatabaseConnection(); return conn.query("SELECT * FROM users"); } // Automatically closed async function asyncWork() { await using resource = new AsyncResource(); } // Disposed with await ``` Use for: connections, file handles, locks, transactions. </resource_management> <satisfies_operator> Validate type without widening (TS 4.9+): ```typescript const config = { port: 3000, host: "localhost", } satisfies Record<string, string | number>; config.port; // number (not string | number) const routes = { home: "/", user: "/user/:id", } as const satisfies Record<string, string>; type HomeRoute = typeof routes.home; // '/' ``` </satisfies_operator> <const_type_parameters> Preserve literals through generics (TS 5.0+): ```typescript function makeTuple<const T extends readonly unknown[]>(...args: T): T { return args; } const result = makeTuple("a", "b", "c"); // ['a', 'b', 'c'] not string[] ``` </const_type_parameters> <inferred_predicates> TS 5.5+ auto-infers type predicates: ```typescript function isString(x: unknown) { return typeof x === "string"; } // Inferred: (x: unknown) => x is string const strings = values.filter(isString); // string[] ``` </inferred_predicates> <template_literals> Pattern matching at type level: ```typescript type Route = `/${string}`; type ApiRoute = `/api/v${number}/${string}`; type ExtractParams<T extends string> = T extends `${string}:${infer P}/${infer R}` ? P | ExtractParams<`/${R}`> : T extends `${string}:${infer P}` ? P : never; type Params = ExtractParams<"/user/:id/post/:postId">; // 'id' | 'postId' ``` See [modern-features.md](references/modern-features.md) for TS 5.5-5.8. </template_literals> ## Zod Validation Schema = runtime validation + TypeScript type. <zod_core> ```typescript import { z } from "zod"; const UserSchema = z.object({ id: z.string().uuid(), email: z.string().email(), name: z.string().min(1).max(100), }); type User = z.infer<typeof UserSchema>; // safeParse preferred const result = UserSchema.safeParse(data); if (!result.success) { console.error(result.error.issues); return; } const user = result.data; ``` </zod_core> <zod_patterns> **Discriminated unions** (preferred over z.union): ```typescript const ApiResponse = z.discriminatedUnion("type", [ z.object({ type: z.literal("success"), data: z.unknown() }), z.object({ type: z.literal("error"), code: z.string(), message: z.string() }), ]); ``` **Environment variables**: ```typescript const EnvSchema = z.object({ NODE_ENV: z .enum(["development", "production", "test"]) .default("development"), DATABASE_URL: z.string().url(), PORT: z.coerce.number().int().positive().default(3000), }); const env = EnvSchema.parse(process.env); ``` **API validation (Hono)**: ```typescript import { zValidator } from "@hono/zod-validator"; app.post("/users", zValidator("json", UserSchema), (c) => { const user = c.req.valid("json"); return c.json(user); }); ``` See: - [zod-building-blocks.md](references/zod-building-blocks.md) - primitives, refinements, transforms - [zod-schemas.md](references/zod-schemas.md) - composition patterns - [zod-integration.md](references/zod-integration.md) - API/form/env integration </zod_patterns> ## Type Guards ```typescript // User-defined function isString(v: unknown): v is string { return typeof v === "string"; } // Assertion function assertString(v: unknown): asserts v is string { if (typeof v !== "string") throw new TypeError("Expected string"); } // With noUncheckedIndexedAccess const users: User[] = getUsers(); const first = users[0]; // User | undefined if (first !== undefined) processUser(first); ``` See [advanced-types.md](references/advanced-types.md) for utilities. ## TSDoc Types show structure. TSDoc shows intent. Critical for AI agents. ```typescript /** * Authenticates user and returns session token. * @param credentials - User login credentials * @returns Session token valid for 24 hours * @throws {AuthenticationError} Invalid credentials * @example * const token = await authenticate({ email, password }); */ export async function authenticate( credentials: Credentials ): Promise<SessionToken>; ``` Document: all exports, parameters with constraints, thrown errors, non-obvious returns. See [tsdoc-patterns.md](references/tsdoc-patterns.md) for comprehensive guide. <rules> ALWAYS: - Strict TypeScript config enabled - Type-only imports: `import type { User } from './types'` - Const assertions for literal types - Exhaustive matching with `assertNever` - Runtime validation at boundaries (Zod) - Branded types for domain/sensitive data - Result types for error-prone operations - `satisfies` for literal inference - `using` for resources with cleanup - TSDoc on all exports
GitHub에서 보기
이 SKILL.md는 매우 커서 SkillsMP가 여기에는 첫 섹션만 미리 보여줍니다. GitHub에서 보기