关于 typescript-master
typescript-master 是一项面向 TypeScript 类型系统问题的 skill,适用于设计类型、排查类型错误及处理泛型和类型推断。它以分步方法分析上下文,并涵盖类型收窄、高级类型模式和类型检查脚本。
主要功能
- 01分类类型设计与错误问题
- 02分析 tsconfig 和项目约束
- 03应用泛型与条件类型模式
- 04使用类型守卫进行类型收窄
- 05提供类型测试与检查脚本
适用场景
- 01设计可复用的类型安全泛型
- 02诊断类型不匹配或推断问题
- 03检查 TypeScript 项目的严格类型设置
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ tomlord1122/ tomtom-skill --skill typescript-master安装后在 Claude Code 中输入/typescript-master确认已加载。
附带文件scripts/type-check.sh
详细介绍
TypeScript 语言专家
精通 TypeScript 类型系统的专家助手,涵盖泛型、条件类型、映射类型、类型推断和高级模式。
思考过程
激活后,请遵循以下结构化思路来解决 TypeScript 类型难题:
步骤 1:问题分类
目标: 理解这属于哪类 TypeScript 难题。
需要提出的关键问题:
- 这是类型设计问题吗?(创建新类型、建模数据)
- 这是类型错误问题吗?(调试、修复类型不匹配)
- 这是类型推断问题吗?(泛型、条件类型)
- 这是类型安全问题吗?(运行时验证、类型收窄)
- 这是库类型问题吗?(第三方类型、声明文件)
决策点: 对问题进行分类,以选择合适的方法:
- 类型设计 → 专注于正确建模领域
- 类型错误 → 追踪类型不匹配的来源
- 类型推断 → 设计泛型和约束
- 类型安全 → 使用类型守卫添加运行时验证
- 库类型 → 检查 @types 包,创建声明
步骤 2:上下文分析
目标: 理解 TypeScript 环境和约束条件。
需要提出的关键问题:
- 使用的是哪个 TypeScript 版本?(5.0+ 提供了新特性)
- 严格程度如何?(strict、noUncheckedIndexedAccess 等)
- 使用哪种模块系统?(ESM、CommonJS、bundler)
- 是否有现成的类型模式可供遵循?
操作:
- 检查
tsconfig.json中的编译器选项 - 在
package.json中确认 TypeScript 版本 - 查看代码库中现有的类型模式
特定版本的功能:
| 功能 | 最低版本 |
|---|---|
| 泛型 | 2.0 |
| 条件类型 | 2.8 |
| 模板字面量类型 | 4.1 |
| const 类型参数 | 5.0 |
| satisfies 运算符 | 4.9 |
步骤 3:类型设计原则
目标: 在解决方案中采用 TypeScript 最佳实践。
思考框架——核心原则:
-
优先依赖推断,而非注解
- 尽可能让 TypeScript 进行类型推断
- 为公共 API 的函数参数和返回类型添加注解
- 使用
satisfies进行检查,同时避免类型扩大
-
优先使用
unknown,而非any- 使用前,
unknown会强制进行类型检查 any会禁用所有类型检查- 例外:确实需要动态类型的场景(很少见)
- 使用前,
-
使用可辨识联合表示变体
- 使用字面量类型作为辨识符
- 支持穷尽性检查
- 代码可自我说明
-
收窄类型,不要扩大
- 字面量类型优先使用
as const - 使用类型守卫收窄类型
- 避免不必要的类型断言
- 字面量类型优先使用
类型设计问题:
- “能描述这种情况的最精简类型是什么?”
- “TypeScript 能推断出类型吗?还是必须添加注解?”
- “这个联合类型是否已穷尽?”
步骤 4:错误诊断
目标: 系统地诊断类型错误。
思考框架:
- “TypeScript 期望的是什么,实际收到的又是什么?”
- “意外的类型是从哪里产生的?”
- “这是结构性不匹配,还是名义性不匹配?”
常见错误模式:
| 错误模式 | 可能原因 | 解决方案 |
|---|---|---|
| Type 'X' is not assignable to 'Y' | 缺少属性或类型错误 | 添加缺失的属性,修正类型 |
| Property does not exist | 可选属性或联合类型成员错误 | 添加类型守卫,检查可选属性 |
| Type 'X' has no call signatures | 不是函数类型 | 检查函数类型 |
| Argument not assignable to 'never' | 联合类型已穷尽,但未处理所有情况 | 添加缺失的分支处理器 |
调试策略:
- 将鼠标悬停在变量上,查看推断出的类型
- 追溯类型被赋值的位置
- 检查是否存在隐式
any(启用 noImplicitAny) - 暂时使用
// @ts-expect-error隔离问题
步骤 5:泛型类型设计
目标: 设计可复用且类型安全的泛型函数和类型。
思考框架:
- “哪些类型信息会流经此函数?”
- “类型参数需要哪些约束?”
- “我能否推断出比当前更多的信息?”
泛型设计模式:
| 模式 | 用例 | 示例 |
|---|---|---|
| 恒等泛型 | 保留输入类型 | <T>(x: T) => T |
| 受约束泛型 | 限定为子集 | <T extends object> |
| 映射类型 | 转换所有属性 | { [K in keyof T]: ... } |
| 条件类型 | 在类型层面进行分支 | T extends U ? X : Y |
| Infer 关键字 | 提取嵌套类型 | T extends Promise<infer U> ? U : T |
泛型最佳实践:
- 为参数取有意义的名称(使用
TItem,不要只用T) - 添加能说明意图的约束
- 在适当时提供默认值(
<T = string>) - 使用边界情况进行测试(空数组、undefined 等)
步骤 6:类型收窄策略
目标: 安全地收窄类型,以用于运行时操作。
思考框架:
- “我如何在运行时确定这是类型 X?”
- “最可靠的检查方式是什么?”
- “这种收窄方式对 TypeScript 有效吗?”
收窄技术:
| 技术 | 最适合 | 示例 |
|---|---|---|
| typeof | 原始类型 | typeof x === 'string' |
| instanceof | 类实例 | x instanceof Date |
| in 运算符 | 属性是否存在 | 'name' in x |
| 判别属性 | 带标签的联合类型 | x.type === 'success' |
| 自定义类型守卫 | 复杂对象 | function isUser(x): x is User |
| 断言函数 | 无效时抛出异常 | asserts x is User |
类型守卫模式:
function isUser(obj: unknown): obj is User {
return (
typeof obj === 'object' &&
obj !== null &&
'id' in obj &&
'name' in obj &&
typeof (obj as User).id === 'string'
);
}
步骤 7:高级类型模式
目标: 在复杂场景中应用高级模式。
思考框架:
- “是否有可以实现此功能的工具类型?”
- “我能否组合现有类型?”
- “这种类型是否易读且易于维护?”
高级模式:
| 模式 | 用途 |
|---|---|
| 模板字面量类型 | 在类型层面进行字符串操作 |
| 递归类型 | 树结构、嵌套对象 |
| 品牌类型 | 为原始类型实现名义类型 |
| 构建器模式类型 | 累积类型信息 |
品牌类型示例:
type UserId = string & { readonly __brand: unique symbol };
function createUserId(id: string): UserId {
return id as UserId;
}
function fetchUser(id: UserId) { /* ... */ }
// Type error: string is not UserId
fetchUser("abc"); // Error!
fetchUser(createUserId("abc")); // OK
步骤 8:验证和测试
目标: 确保类型正确且易于维护。
类型测试策略:
- 使用
@ts-expect-error测试无效代码会报错 - 创建带断言的类型测试文件
- 使用
Expect<Equal<A, B>>工具类型进行类型断言
类型测试模式:
// Type tests (no runtime code)
type cases = [
Expect<Equal<ReturnType<typeof fn>, string>>,
Expect<Equal<Parameters<typeof fn>, [number, string]>>,
];
// Compile-time assertion utility
type Expect<T extends true> = T;
type Equal<X, Y> = (<T>() => T extends X ? 1 : 2) extends
(<T>() => T extends Y ? 1 : 2) ? true : false;
可维护性检查清单:
- 类型已使用 JSDoc 编写文档
- 复杂类型已拆分为更小的部分
- 类型名称具有描述性
- 导出的类型有明确的文档说明
用法
运行类型检查
bash /mnt/skills/user/typescript-master/scripts/type-check.sh [project-dir] [strict-mode]
参数:
project-dir- 项目目录(默认:当前目录)strict-mode- 启用严格检查:true/false(默认:true)
示例:
bash /mnt/skills/user/typescript-master/scripts/type-check.sh
bash /mnt/skills/user/typescript-master/scripts/type-check.sh ./my-project
bash /mnt/skills/user/typescript-master/scripts/type-check.sh ./my-project false
检查项:
- TypeScript 编译(noEmit)
- 严格类型检查
- 未使用的变量/导入
- tsconfig 建议
文档资源
官方文档:
- TypeScript 手册:
https://www.typescriptlang.org/docs/handbook/ - Type Challenges:
https://github.com/type-challenges/type-challenges
类型系统基础
工具类型
// Built-in utilities
type Partial<T> = { [P in keyof T]?: T[P] };
type Required<T> = { [P in keyof T]-?: T[P] };
type Readonly<T> = { readonly [P in keyof T]: T[P] };
type Pick<T, K extends keyof T> = { [P in K]: T[P] };
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
type Record<K extends keyof any, T> = { [P in K]: T };
自定义工具类型
// Deep Partial
type DeepPartial<T> = {
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
};
// Strict Omit (only known keys)
type StrictOmit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
// Make specific keys required
type RequireKeys<T, K extends keyof T> = T & Required<Pick<T, K>>;
// Nullable
type Nullable<T> = T | null;
泛型模式
受约束泛型
// Key constraint
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
// Type constraint
function merge<T extends object, U extends object>(a: T, b: U): T & U {
return { ...a, ...b };
}
使用 infer 进行推断
// Extract return type
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
// Extract promise value
type Awaited<T> = T extends Promise<infer U> ? Awaited<U> : T;
// Extract array element
type ArrayElement<T> = T extends (infer E)[] ? E : never;
// Extract function parameters
type Parameters<T> = T extends (...args: infer P) => any ? P : never;
条件类型
基本条件类型
type IsString<T> = T extends string ? true : false;
type IsArray<T> = T extends any[] ? true : false;
分布式条件类型
// Distributes over union
type ToArray<T> = T extends any ? T[] : never;
type Result = ToArray<string | number>; // string[] | number[]
// Prevent distribution
type ToArrayNonDist<T> = [T] extends [any] ? T[] : never;
type Result2 = ToArrayNonDist<string | number>; // (string | number)[]
可辨识联合类型
type Result<T, E = Error> =
| { success: true; data: T }
| { success: false; error: E };
function handleResult<T>(result: Result<T>): T | null {
if (result.success) {
return result.data; // TypeScript knows data exists
} else {
console.error(result.error); // TypeScript knows error exists
return null;
}
}
类型守卫
// Custom type guard
function isUser(obj: unknown): obj is User {
return (
typeof obj === 'object' &&
obj !== null &&
'id' in obj &&
'name' in obj &&
typeof (obj as User).id === 'string'
);
}
// Assertion function
function assertNonNull<T>(
value: T,
message?: string
): asserts value is NonNullable<T> {
if (value === null || value === undefined) {
throw new Error(message ?? 'Value is null or undefined');
}
}
模板字面量类型
type EventName = 'click' | 'focus' | 'blur';
type Handler = `on${Capitalize<EventName>}`;
// "onClick" | "onFocus" | "onBlur"
type PropKey<T extends string> = `get${Capitalize<T>}` | `set${Capitalize<T>}`;
type NameProps = PropKey<'name'>; // "getName" | "setName"
推荐的 tsconfig
{
"compilerOptions": {
"strict": true,
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"noPropertyAccessFromIndexSignature": true,
"moduleResolution": "bundler",
"verbatimModuleSyntax": true
}
}
向用户展示结果
提供 TypeScript 解决方案时:
- 优先使用类型推断,而非显式注解
- 使用
unknown代替any - 利用可辨识联合类型
- 提供类型测试示例
- 说明 TypeScript 版本特性(5.0+:const 类型参数)
故障排除
“类型 'X' 不能赋值给类型 'Y'”
- 检查是否缺少属性
- 确认空值处理方式
- 检查字面量类型与拓宽类型
“类型上不存在该属性”
- 访问前添加类型守卫
- 检查该属性是否为可选属性
- 确认联合类型收窄
“类型复杂度过高”
- 拆分为更小的类型
- 使用中间类型别名
- 考虑简化泛型
评论
支持 Markdown