typescript
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseTypeScript Skill
TypeScript 实践技能
Load with: base.md
需结合base.md使用
Strict Mode (Non-Negotiable)
严格模式(强制要求)
json
// tsconfig.json
{
"compilerOptions": {
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
}
}json
// tsconfig.json
{
"compilerOptions": {
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
}
}Project Structure
项目结构
project/
├── src/
│ ├── core/ # Pure business logic
│ │ ├── types.ts # Domain types/interfaces
│ │ ├── services/ # Pure functions
│ │ └── index.ts # Public API
│ ├── infra/ # Side effects
│ │ ├── api/ # HTTP handlers
│ │ ├── db/ # Database operations
│ │ └── external/ # Third-party integrations
│ └── utils/ # Shared utilities
├── tests/
│ ├── unit/
│ └── integration/
├── package.json
├── tsconfig.json
└── CLAUDE.mdproject/
├── src/
│ ├── core/ # Pure business logic
│ │ ├── types.ts # Domain types/interfaces
│ │ ├── services/ # Pure functions
│ │ └── index.ts # Public API
│ ├── infra/ # Side effects
│ │ ├── api/ # HTTP handlers
│ │ ├── db/ # Database operations
│ │ └── external/ # Third-party integrations
│ └── utils/ # Shared utilities
├── tests/
│ ├── unit/
│ └── integration/
├── package.json
├── tsconfig.json
└── CLAUDE.mdTooling (Required)
必备工具链
json
// package.json scripts
{
"scripts": {
"lint": "eslint src/ --ext .ts,.tsx",
"typecheck": "tsc --noEmit",
"test": "jest",
"test:coverage": "jest --coverage",
"format": "prettier --write 'src/**/*.ts'"
}
}javascript
// 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],
}
}
);json
// package.json scripts
{
"scripts": {
"lint": "eslint src/ --ext .ts,.tsx",
"typecheck": "tsc --noEmit",
"test": "jest",
"test:coverage": "jest --coverage",
"format": "prettier --write 'src/**/*.ts'"
}
}javascript
// 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],
}
}
);Testing with Jest
使用Jest进行测试
typescript
// tests/unit/services/user.test.ts
import { calculateTotal } from '../../../src/core/services/pricing';
describe('calculateTotal', () => {
it('returns sum of item prices', () => {
// Arrange
const items = [{ price: 10 }, { price: 20 }];
// Act
const result = calculateTotal(items);
// Assert
expect(result).toBe(30);
});
it('returns zero for empty array', () => {
expect(calculateTotal([])).toBe(0);
});
it('throws on invalid item', () => {
expect(() => calculateTotal([{ invalid: 'item' }])).toThrow();
});
});typescript
// tests/unit/services/user.test.ts
import { calculateTotal } from '../../../src/core/services/pricing';
describe('calculateTotal', () => {
it('returns sum of item prices', () => {
// Arrange
const items = [{ price: 10 }, { price: 20 }];
// Act
const result = calculateTotal(items);
// Assert
expect(result).toBe(30);
});
it('returns zero for empty array', () => {
expect(calculateTotal([])).toBe(0);
});
it('throws on invalid item', () => {
expect(() => calculateTotal([{ invalid: 'item' }])).toThrow();
});
});GitHub Actions
GitHub Actions 配置
yaml
name: TypeScript Quality Gate
on: [push, pull_request]
jobs:
quality:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install dependencies
run: npm ci
- name: Lint
run: npm run lint
- name: Type Check
run: npm run typecheck
- name: Test with Coverage
run: npm run test:coverage
- name: Coverage Threshold (80%)
run: npm run test:coverage -- --coverageThreshold='{"global":{"branches":80,"functions":80,"lines":80,"statements":80}}'yaml
name: TypeScript Quality Gate
on: [push, pull_request]
jobs:
quality:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install dependencies
run: npm ci
- name: Lint
run: npm run lint
- name: Type Check
run: npm run typecheck
- name: Test with Coverage
run: npm run test:coverage
- name: Coverage Threshold (80%)
run: npm run test:coverage -- --coverageThreshold='{"global":{"branches":80,"functions":80,"lines":80,"statements":80}}'Pre-Commit Hooks
提交前钩子
Using Husky + lint-staged:
bash
npm install -D husky lint-staged
npx husky initjson
// package.json
{
"lint-staged": {
"*.{ts,tsx}": [
"eslint --fix",
"prettier --write"
]
}
}bash
undefined使用Husky + lint-staged:
bash
npm install -D husky lint-staged
npx husky initjson
// package.json
{
"lint-staged": {
"*.{ts,tsx}": [
"eslint --fix",
"prettier --write"
]
}
}bash
undefined.husky/pre-commit
.husky/pre-commit
npx lint-staged
npx tsc --noEmit
npm run test -- --onlyChanged --passWithNoTests
This runs on every commit:
1. ESLint + Prettier on staged files
2. Type check entire project
3. Tests for changed files only
---npx lint-staged
npx tsc --noEmit
npm run test -- --onlyChanged --passWithNoTests
这会在每次提交时执行以下操作:
1. 对暂存文件执行ESLint修复与Prettier格式化
2. 对整个项目进行类型检查
3. 仅运行变更文件的测试(无测试文件时也会通过)
---Type Patterns
类型设计模式
Discriminated Unions for Results
用于结果处理的可区分联合类型
typescript
type Result<T> =
| { ok: true; value: T }
| { ok: false; error: string };
function parseUser(data: unknown): Result<User> {
// Type-safe error handling without exceptions
}typescript
type Result<T> =
| { ok: true; value: T }
| { ok: false; error: string };
function parseUser(data: unknown): Result<User> {
// Type-safe error handling without exceptions
}Branded Types for IDs
用于ID的品牌化类型
typescript
type UserId = string & { readonly brand: unique symbol };
type OrderId = string & { readonly brand: unique symbol };
// Can't accidentally pass UserId where OrderId expected
function getOrder(orderId: OrderId): Order { ... }typescript
type UserId = string & { readonly brand: unique symbol };
type OrderId = string & { readonly brand: unique symbol };
// Can't accidentally pass UserId where OrderId expected
function getOrder(orderId: OrderId): Order { ... }Const Assertions for Literals
字面量的Const断言
typescript
const STATUSES = ['pending', 'active', 'closed'] as const;
type Status = typeof STATUSES[number]; // 'pending' | 'active' | 'closed'typescript
const STATUSES = ['pending', 'active', 'closed'] as const;
type Status = typeof STATUSES[number]; // 'pending' | 'active' | 'closed'Zod for Runtime Validation
使用Zod进行运行时验证
typescript
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
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 Anti-Patterns
TypeScript 反模式
- ❌ type - use
anyand narrowunknown - ❌ Type assertions () - use type guards
as - ❌ Non-null assertions () - handle null explicitly
! - ❌ without explanation
@ts-ignore - ❌ Enums - use const objects or union types
- ❌ Classes for data - use interfaces/types
- ❌ Default exports - use named exports
- ❌ 使用类型 - 应使用
any并进行类型收窄unknown - ❌ 类型断言() - 应使用类型守卫
as - ❌ 非空断言() - 应显式处理null
! - ❌ 无说明的
@ts-ignore - ❌ 使用枚举(Enums) - 应使用常量对象或联合类型
- ❌ 使用类存储数据 - 应使用接口/类型别名
- ❌ 默认导出 - 应使用命名导出