plugins/languages/typescript/skills/types/SKILL.md
TypeScript 高级类型系统规范,覆盖 discriminated unions、模板字面量类型、条件 / mapped types、TS 5.5 inferred predicates 类型守卫、branded types、satisfies、Zod 4 / Valibot 运行时验证。同时给出 JS-only 项目通过 JSDoc + tsc --checkJs 获得类型保护的方案。Use when 设计复杂类型、类型体操、API 类型契约、运行时校验、JS 项目加类型保护,或用户提到 "类型系统"、"discriminated union"、"Zod schema"、"类型守卫"、"branded type"、"JSDoc 类型"。
npx skillsauth add lazygophers/ccplugin typescript-typesInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
本 skill 同时覆盖 JavaScript 项目;JS 项目用 JSDoc + tsc --checkJs 获得 80% 类型保护,见末尾兜底章节。
类型系统两大用途:建模业务状态 (discriminated unions / branded types) + 验证外部输入 (Zod / Valibot)。
type UserDTO = { id: string; name: string }; // PascalCase
type ApiResponse<T> = { data: T; status: number };
type Status = "active" | "inactive" | "pending";
// 禁止 I 前缀
// type IUser = {};
type AsyncState<T> =
| { status: "idle" }
| { status: "loading" }
| { status: "success"; data: T }
| { status: "error"; error: Error };
function render<T>(state: AsyncState<T>): string {
switch (state.status) {
case "idle": return "Waiting...";
case "loading": return "Loading...";
case "success": return `Got: ${JSON.stringify(state.data)}`;
case "error": return `Error: ${state.error.message}`;
default: {
const _exhaustive: never = state; // 穷举检查
return _exhaustive;
}
}
}
function createConfig<const T extends Record<string, unknown>>(c: T): T {
return c;
}
const cfg = createConfig({ api: "/v1", timeout: 3000 });
// typeof cfg = { readonly api: "/v1"; readonly timeout: 3000 }
type HTTPMethod = "GET" | "POST" | "PUT" | "DELETE";
type APIRoute = `/api/${string}`;
type Endpoint = `${HTTPMethod} ${APIRoute}`;
type EventName = `on${Capitalize<string>}`;
// TS 5.5+: 推断类型谓词
function isNonNullable<T>(v: T): v is NonNullable<T> {
return v !== null && v !== undefined;
}
const users: (User | null)[] = [u1, null, u2];
const valid = users.filter(isNonNullable); // User[]
// 显式自定义类型守卫
function isUser(v: unknown): v is User {
return typeof v === "object" && v !== null
&& "id" in v && typeof (v as { id: unknown }).id === "string";
}
import { z } from "zod";
const UserSchema = z.object({
id: z.uuid(), // Zod 4: 顶层 helper
name: z.string().min(1).max(100),
email: z.email(),
role: z.enum(["admin", "user", "guest"]),
metadata: z.record(z.string(), z.unknown()).optional(),
});
type User = z.infer<typeof UserSchema>; // schema-first 类型
const r = UserSchema.safeParse(data);
if (!r.success) console.error(z.treeifyError(r.error));
// 派生 schema
const CreateUserSchema = UserSchema.omit({ id: true }).extend({
password: z.string().min(8).regex(/[A-Z]/).regex(/[0-9]/),
});
替代选择:Valibot (更小 bundle,函数式 API,<2KB),用于体积敏感场景。
type Brand<T, B extends string> = T & { readonly __brand: B };
type UserId = Brand<string, "UserId">;
type PostId = Brand<string, "PostId">;
function getUser(id: UserId): Promise<User> { /* ... */ }
// getUser("123" as UserId); // OK
// getUser("123" as PostId); // Error
const routes = {
home: "/",
about: "/about",
user: "/user/:id",
} satisfies Record<string, string>;
// typeof routes.home = "/" (非 string)
// 内置:Partial / Required / Pick / Omit / Record / Readonly / Awaited / ReturnType / Parameters
// type-fest 推荐补充:SetOptional, SetRequired, Merge, CamelCase, etc.
type ExtractPromise<T> = T extends Promise<infer U> ? U : T;
type DeepReadonly<T> = { readonly [K in keyof T]: DeepReadonly<T[K]> };
JS 项目无需切 TS 也能获得 80% 类型保护:
// jsconfig.json
{
"compilerOptions": {
"target": "ES2025",
"module": "NodeNext",
"allowJs": true,
"checkJs": true,
"strict": true,
"noEmit": true
},
"include": ["src/**/*"]
}
// JSDoc 类型定义
/**
* @typedef {Object} User
* @property {string} id
* @property {string} name
* @property {'admin' | 'user' | 'guest'} role
*/
/** @typedef {{ status: 'idle' } | { status: 'loading' } | { status: 'success'; data: User } | { status: 'error'; error: Error }} AsyncState */
/**
* @param {string} id
* @returns {Promise<User>}
*/
export async function getUser(id) {
const r = await fetch(`/api/users/${id}`);
return /** @type {User} */ (await r.json());
}
// Zod 在 JS 同样可用,z.infer 通过 JSDoc 桥接
import { z } from 'zod';
const UserSchema = z.object({ id: z.uuid(), name: z.string() });
/** @typedef {z.infer<typeof UserSchema>} User */
pnpm tsc --noEmit # 当 linter 跑
渐进迁移:JSDoc → 文件级 .ts → 局部启 strict → 全量 strict。
| 现象 | 问题 | 严重 |
|------|------|------|
| any | 用 unknown + 守卫 | 高 |
| 未穷举的 switch | DU 遗漏分支 | 高 |
| as 强转 | 可能隐藏错误 (仅边界用) | 中 |
| 外部数据无 Zod | 运行时类型不安全 | 高 |
| 递归类型 > 5 层 | 编译性能 | 中 |
| I 前缀 | C# 约定 | 低 |
| enum | tree-shake 不友好 | 中 |
| JS 项目无 checkJs | 失去类型保护 | 中 |
any,外部数据 Zod / Valibot 验证never 穷举import type 分离类型 (TS)extends 约束satisfiesI 前缀checkJs: true + JSDoctools
--- name: trellisx-workspace description: 维护 `.trellis/task.md` 任务看板 —— trellis 缺的跨任务总览。**一个表格, 一行一个任务**, 列为 id/名称/描述/状态/阶段/进度/worktree (状态/阶段中文显示)。在 task create/start/阶段切换/archive 后**及时更新**对应行; 并**自动清理超 7 天的已完成行**防膨胀。保持看板与 task.json 实时一致。 when_to_use: 维护 / 创建 / 更新 `.trellis/task.md` 任务看板时; task 生命周期任一节点 (create/start/阶段推进/archive) 之后同步看板时; 用户问"当前有哪些任务 / 任务进度 / 任务看板"时。被 trellisx-flow 与 trellisx-apply 注入的流程引用。 user-invocable: true argument-hint: [show|update|sync|cleanup ...] [task id] arguments:
testing
强制以 Trellis task 闭环处理用户指定的请求 (自判新建/并入 → plan→exec→check→finish 全程不跳步)。**仅用户显式主动调用** (/trellisx-flow 或明确要求"强制走 task 处理这个"); **禁止自动 / 被动 / 推断式调用** —— 不要因为某个请求"看起来该建 task"就自动触发本 skill, 那是 apply 注入的 no_task 倾向的职责。
testing
把 强推task + subtask拆分 + worktree隔离 + 闭环收尾 四维度增量注入当前项目 .trellis/ (workflow.md 的 no_task/planning/in_progress 块 + spec 背书文档 + trellis 生命周期 hook worktree 自动化)。强推 task 与闭环为纯 prompt 软约束 (非平台 hook 硬拦截)。**纯增量追加, 绝不替换 trellis 原生文本** (no_task 分类+征同意/check/finish/前缀全保留)。幂等 (marker 包裹)。
development
Claude Code 会话历史整理 — 扫 ~/.claude/projects/**/*.jsonl 全部 session transcripts, 提取学习增量 (用户校正/决策/踩坑/L0 规则) → 全局记忆库 ~/.cortex/.wiki/memory/. 默认 --apply 落盘 (--dry-run opt-in 仅出 JSON plan 预览). 与 cortex-extract (L4-inbox 内部) 互补.