TypeScript技能 typescript

TypeScript 技能涵盖了使用 TypeScript 进行严格模式编程,结合 ESLint 和 Jest 进行代码质量和测试保障的最佳实践。关键词包括:TypeScript, ESLint, Jest, 代码质量,测试覆盖率。

前端开发 0 次安装 0 次浏览 更新于 3/5/2026

TypeScript 技能

加载与:base.md


严格模式(不可协商)

// tsconfig.json
{
  "compilerOptions": {
    "strict": true,
    "noImplicitAny": true,
    "strictNullChecks": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noImplicitReturns": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  }
}

项目结构

project/
├── src/
│   ├── core/               # 纯业务逻辑
│   │   ├── types.ts        # 领域类型/接口
│   │   ├── services/       # 纯函数
│   │   └── index.ts        # 公共API
│   ├── infra/              # 副作用
│   │   ├── api/            # HTTP处理器
│   │   ├── db/             # 数据库操作
│   │   └── external/       # 第三方集成
│   └── utils/              # 共享工具
├── tests/
│   ├── unit/
│   └── integration/
├── package.json
├── tsconfig.json
└── CLAUDE.md

工具(必需)

// package.json 脚本
{
  "scripts": {
    "lint": "eslint src/ --ext .ts,.tsx",
    "typecheck": "tsc --noEmit",
    "test": "jest",
    "test:coverage": "jest --coverage",
    "format": "prettier --write 'src/**/*.ts'"
  }
}
// eslint.config.js
import eslint from '@eslint/js';
import tseslint from 'typescript-eslint';

export default tseslint.config(
  eslint.configs.recommended,
  ...tseslint.configs.strictTypeChecked,
  {
    rules: {
      '@typescript-eslint/no-explicit-any': 'error',
      '@typescript-eslint/explicit-function-return-type': 'error',
      'max-lines-per-function': ['error', 20],
      'max-depth': ['error', 2],
      'max-params': ['error', 3],
    }
  }
);

使用 Jest 测试

// tests/unit/services/user.test.ts
import { calculateTotal } from '../../../src/core/services/pricing';

describe('calculateTotal', () => {
  it('返回项目价格的总和', () => {
    // 安排
    const items = [{ price: 10 }, { price: 20 }];

    // 行动
    const result = calculateTotal(items);

    // 断言
    expect(result).toBe(30);
  });

  it('返回空数组为零', () => {
    expect(calculateTotal([])).toBe(0);
  });

  it('对无效项目抛出异常', () => {
    expect(() => calculateTotal([{ invalid: 'item' }])).toThrow();
  });
});

GitHub Actions

name: TypeScript 质量门

on: [push, pull_request]

jobs:
  quality:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: 设置 Node
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          
      - name: 安装依赖
        run: npm ci
        
      - name: 代码检查
        run: npm run lint
        
      - name: 类型检查
        run: npm run typecheck
        
      - name: 测试覆盖率
        run: npm run test:coverage
        
      - name: 覆盖率阈值(80%)
        run: npm run test:coverage -- --coverageThreshold='{"global":{"branches":80,"functions":80,"lines":80,"statements":80}}'

预提交钩子

使用 Husky + lint-staged:

npm install -D husky lint-staged
npx husky init
// package.json
{
  "lint-staged": {
    "*.{ts,tsx}": [
      "eslint --fix",
      "prettier --write"
    ]
  }
}
# .husky/pre-commit
npx lint-staged
npx tsc --noEmit
npm run test -- --onlyChanged --passWithNoTests

这在每次提交时运行:

  1. ESLint + Prettier 在暂存文件上
  2. 整个项目的类型检查
  3. 仅针对更改的文件进行测试

类型模式

结果的区分联合

type Result<T> =
  | { ok: true; value: T }
  | { ok: false; error: string };

function parseUser(data: unknown): Result<User> {
  // 无异常的类型安全错误处理
}

ID的品牌类型

type UserId = string & { readonly brand: unique symbol };
type OrderId = string & { readonly brand: unique symbol };

// 不能意外地将 UserId 传递到 OrderId 预期的地方
function getOrder(orderId: OrderId): Order { ... }

字面量的 Const 断言

const STATUSES = ['pending', 'active', 'closed'] as const;
type Status = typeof STATUSES[number]; // 'pending' | 'active' | 'closed'

Zod 用于运行时验证

import { z } from 'zod';

const UserSchema = z.object({
  email: z.string().email(),
  name: z.string().min(1).max(100),
});

type User = z.infer<typeof UserSchema>;

TypeScript 反模式

  • any 类型 - 使用 unknown 并缩小
  • ❌ 类型断言(as) - 使用类型守卫
  • ❌ 非空断言(!) - 显式处理空值
  • @ts-ignore 无解释
  • ❌ 枚举 - 使用 const 对象或联合类型
  • ❌ 数据的类 - 使用接口/类型
  • ❌ 默认导出 - 使用命名导出