mcpField
T
typescript

typescript

github.com/alexander-danilenko开发1894 次安装2 周前更新
代码仓库

关于 typescript

面向 TypeScript 5+ 项目的编码规范 Skill,帮助开发者编写或修改代码时加强类型建模、明确处理状态,并采用实用的编译器设置。

主要功能

  1. 01用品牌类型和联合类型建模领域
  2. 02优先使用 satisfies 而非类型断言
  3. 03用 const 对象替代 enum
  4. 04收窄 unknown 并隔离 any
  5. 05分离仅类型导入
  6. 06配置严格的编译器检查

适用场景

  1. 01为新编写的 TypeScript 代码应用规范
  2. 02检查代码变更中的类型安全问题
  3. 03设定 TypeScript 编译器基线

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/alexander-danilenko/cortex-ai-skills --skill typescript

安装后在 Claude Code 中输入/typescript确认已加载。

详细介绍

TypeScript

TypeScript 5+ 的团队约定。请应用于你编写或修改的代码;除非另有要求,否则不要为了符合约定而重构未触碰的文件。

约定

  • 用类型建模领域,而不是用检查。 品牌类型(type UserId = string & { readonly __brand: 'UserId' })和可辨识联合会在编译时暴露错误,几乎没有成本。运行时防护则会让同样的错误在生产环境中才被发现。
  • 凡是有状态的情况,都使用可辨识联合——请求生命周期、表单状态、解析结果等。这样编译器会强制处理每种状态,新变体出现时也会作为错误暴露出来,而不是被静默跳过。
  • 优先使用 satisfies,而不是 as。 satisfies 会根据类型进行验证,同时保留较窄的推断字面类型;as 则会绕过检查。只有在你掌握了编译器无法得知的信息时才使用 as,并在注释中说明原因。
  • 优先使用常量对象,而不是 enum。 as const 对象是普通值,不会生成代码,也不会带来名义类型上的意外;enum 会生成运行时代码,而 const enum 在 isolatedModules 下会出问题。
  • any 就是错误报告。 使用 unknown 并缩窄类型。如果边界处确实无法避免 any,就将它隔离在一个适配器函数中,别让它扩散到各个调用点。
  • 单独导入类型(import type { … })。打包器会干净地移除这些导入;混合导入可能会让你原本想移除的运行时依赖继续存在。
  • 让类型推断发挥作用。 为导出接口和公共签名添加类型注解,让内部代码自行推断;过度注解只会增加噪声,并逐渐偏离实际类型。
  • 作为库供他人使用的内容都要提供 .d.ts(declaration: true)。否则,使用者会退回到 any,你的类型工作也就止步于包边界。
  • 单体仓库:使用项目引用和 composite。 增量构建只会重新构建发生变化的部分;单一的扁平 tsconfig 则会在每次编辑时重新编译整个项目。

值得展示的两种惯用写法

只有强制检查穷尽性,它才真正成立。联合类型新增变体时,对它执行 switch 仍然可以顺利编译——assertNever 才能让每个需要更新的位置都报出构建错误:

function assertNever(x: never): never {
  throw new Error(`Unhandled variant: ${JSON.stringify(x)}`);
}

function area(s: Shape): number {
  switch (s.kind) {
    case "circle":
      return Math.PI * s.r ** 2;
    case "rect":
      return s.w * s.h;
    default:
      return assertNever(s); // add a variant → compile error here
  }
}

只有在获取品牌类型的唯一途径是经过验证的函数时,品牌才有意义:

type Brand<T, B> = T & { readonly __brand: B };
type Email = Brand<string, "Email">;

export function toEmail(raw: string): Email {
  if (!raw.includes("@")) throw new Error(`Not an email: ${raw}`);
  return raw as Email;
}

构造函数中的 as 是唯一合理的用法——它位于验证边界,将未经验证的值转换为已验证的值。在其他任何地方将值断言为 Email,都会让整个类型设计失去意义。

tsconfig 基线

strict: true 并未启用所有值得开启的选项。请添加以下选项——每个选项都能捕获默认配置集遗漏的一类错误:

{
  "compilerOptions": {
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "noPropertyAccessFromIndexSignature": true,
    "noImplicitOverride": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "isolatedModules": true,
    "declaration": true,
    "skipLibCheck": true
  }
}

noUncheckedIndexedAccess 是价值最高、也最难采用的选项:它会让 arr[i] 返回 T | undefined,这才符合实际情况。新项目请开启此选项;现有项目则要做好迁移准备。

模块解析:Node 运行输出时使用 NodeNext,由打包器处理时使用 bundler。

评论

支持 Markdown