Skip to content

开发工具配置

本章介绍 TypeScript 项目的本地开发工具配置,确保开发效率和代码质量。

TypeScript 运行和编译

1.1 tsx - 零配置 TypeScript 运行器(推荐)

1.1.1 为什么选择 tsx

  • 零配置:无需任何配置文件即可运行 TypeScript
  • 极速启动:基于 esbuild,比 ts-node 快 10-100x
  • 完美兼容:支持 ESM 和 CommonJS,自动处理路径别名
  • 开发友好:内置 watch 模式,支持环境变量加载

1.1.2 安装和基础使用

bash
# 安装(推荐作为开发依赖)
pnpm add -D tsx

# 直接运行 TypeScript 文件
tsx src/index.ts

# Watch 模式(文件变化自动重启)
tsx watch src/index.ts

# 运行测试文件
tsx test/example.test.ts

# REPL 模式
tsx

1.1.3 package.json 脚本配置

json
// package.json - 单包项目
{
  "scripts": {
    // 开发脚本(使用 tsx)
    "dev": "tsx watch src/index.ts",
    "start": "tsx src/index.ts",
    "test": "tsx --test src/**/*.test.ts",
    
    // 脚本运行
    "script": "tsx",
    "seed": "tsx scripts/seed.ts",
    "migrate": "tsx scripts/migrate.ts",
    
    // 类型检查(仍需要 tsc)
    "type-check": "tsc --noEmit",
    "type-check:watch": "tsc --noEmit --watch",
    
    // 构建(用其他工具)
    "build": "tsup",
    "build:types": "tsc --emitDeclarationOnly --declaration"
  }
}

// 🔍 Monorepo 场景下的协调
// 在 Monorepo 中,包级别可以选择:
// 1. 库包:使用 tsup --watch(需要构建)
// 2. 应用包:使用 tsx watch(直接运行)
// 3. 脚本包:使用 tsx(无需构建)
// 详见 Monorepo 规范:/zh/standards/monorepo

1.1.4 tsx 高级配置

json
// package.json - Node.js 选项传递
{
  "scripts": {
    // 增加内存限制
    "dev": "NODE_OPTIONS='--max-old-space-size=4096' tsx watch src/index.ts",
    
    // 启用源码映射
    "dev:inspect": "tsx watch --inspect src/index.ts",
    
    // 加载环境变量
    "dev:env": "tsx watch --env-file=.env.local src/index.ts",
    
    // 忽略特定文件
    "dev:ignore": "tsx watch --ignore='**/*.test.ts' src/index.ts"
  }
}

1.2 tsc - 类型检查专用

1.2.1 类型检查配置

json
// tsconfig.json - 开发环境优化
{
  "compilerOptions": {
    // 增量编译
    "incremental": true,
    "tsBuildInfoFile": ".tsbuildinfo",
    
    // 跳过库文件检查
    "skipLibCheck": true,
    
    // 只编译修改的文件
    "assumeChangesOnlyAffectDirectDependencies": true
  },
  
  // 包含和排除优化
  "include": ["src/**/*"],
  "exclude": [
    "node_modules",
    "**/*.test.ts",
    "**/*.spec.ts",
    "dist",
    "coverage"
  ]
}

1.2.2 Watch 模式配置

json
// tsconfig.json
{
  "watchOptions": {
    "watchFile": "useFsEvents",  // 使用文件系统事件
    "watchDirectory": "useFsEvents",
    "fallbackPolling": "dynamicPriority",
    "synchronousWatchDirectory": false,
    
    // 排除监听
    "excludeDirectories": [
      "node_modules",
      "dist",
      ".git"
    ],
    "excludeFiles": [
      "**/*.test.ts",
      "**/*.spec.ts"
    ]
  }
}

1.3 类型检查集成

1.3.1 Pre-commit 类型检查

yaml
# .lefthook.yml 或 .huskyrc
pre-commit:
  commands:
    type-check:
      run: npx tsc --noEmit
      glob: "*.{ts,tsx}"

1.3.2 VS Code 集成

json
// .vscode/settings.json
{
  "typescript.tsdk": "./node_modules/typescript/lib",
  "typescript.enablePromptUseWorkspaceTsdk": true,
  
  // 实时类型检查
  "typescript.validate.enable": true,
  "typescript.suggest.autoImports": true,
  "typescript.updateImportsOnFileMove.enabled": "always",
  
  // 代码提示增强
  "typescript.suggest.completeFunctionCalls": true,
  "typescript.preferences.includePackageJsonAutoImports": "on",
  "typescript.preferences.importModuleSpecifier": "shortest",
  
  // 格式化选项
  "typescript.format.enable": true,
  "typescript.format.semicolons": "remove",
  "typescript.preferences.quoteStyle": "single"
}

1.4 增量编译优化

1.4.1 项目引用配置

json
// 根目录 tsconfig.json
{
  "files": [],
  "references": [
    { "path": "./packages/core" },
    { "path": "./packages/utils" },
    { "path": "./apps/web" }
  ]
}

// packages/core/tsconfig.json
{
  "extends": "../../tsconfig.base.json",
  "compilerOptions": {
    "composite": true,
    "outDir": "./dist",
    "rootDir": "./src"
  },
  "references": [
    { "path": "../utils" }
  ]
}

1.4.2 缓存策略

bash
# 利用 TypeScript 缓存
export TSC_COMPILE_ON_ERROR=true  # 即使有错误也继续编译
export TSC_NONPOLLING_WATCHER=true  # 使用非轮询监听器

# 构建脚本中的缓存处理
#!/bin/bash
# build.sh

# 检查缓存有效性
if [ -f ".tsbuildinfo" ]; then
  echo "Using incremental compilation"
else
  echo "First build, no cache available"
fi

# 构建
tsc --build

# 保存缓存信息
echo "Build completed at $(date)" > .build-timestamp

代码质量工具

2.1 ESLint 配置

2.1.1 TypeScript ESLint 配置

javascript
// .eslintrc.js
module.exports = {
  root: true,
  parser: '@typescript-eslint/parser',
  parserOptions: {
    project: './tsconfig.json',
    ecmaVersion: 2022,
    sourceType: 'module'
  },
  
  plugins: [
    '@typescript-eslint',
    'import',
    'unicorn',
    'prettier'
  ],
  
  extends: [
    'eslint:recommended',
    'plugin:@typescript-eslint/recommended',
    'plugin:@typescript-eslint/recommended-requiring-type-checking',
    'plugin:import/typescript',
    'prettier'  // 必须放最后
  ],
  
  rules: {
    // TypeScript 规则
    '@typescript-eslint/explicit-function-return-type': 'off',
    '@typescript-eslint/explicit-module-boundary-types': 'off',
    '@typescript-eslint/no-explicit-any': 'error',
    '@typescript-eslint/no-unused-vars': ['error', {
      argsIgnorePattern: '^_',
      varsIgnorePattern: '^_'
    }],
    '@typescript-eslint/consistent-type-imports': ['error', {
      prefer: 'type-imports',
      fixStyle: 'inline-type-imports'
    }],
    '@typescript-eslint/no-non-null-assertion': 'warn',
    
    // Import 规则
    'import/order': ['error', {
      groups: [
        'builtin',
        'external',
        'internal',
        'parent',
        'sibling',
        'index',
        'type'
      ],
      'newlines-between': 'always',
      alphabetize: {
        order: 'asc',
        caseInsensitive: true
      }
    }],
    
    // 通用规则
    'no-console': ['warn', { allow: ['warn', 'error'] }],
    'prefer-const': 'error',
    'no-param-reassign': 'error'
  },
  
  overrides: [
    // 测试文件规则
    {
      files: ['*.test.ts', '*.spec.ts'],
      env: {
        jest: true
      },
      rules: {
        '@typescript-eslint/no-explicit-any': 'off'
      }
    }
  ],
  
  ignorePatterns: [
    'dist',
    'coverage',
    'node_modules',
    '*.js',
    '*.d.ts'
  ]
}

2.1.2 自定义 ESLint 规则

javascript
// .eslintrc-custom-rules.js
module.exports = {
  rules: {
    // 禁止使用特定的导入
    'no-restricted-imports': ['error', {
      paths: [
        {
          name: 'lodash',
          message: 'Use lodash-es instead'
        },
        {
          name: 'moment',
          message: 'Use date-fns instead'
        }
      ],
      patterns: [
        {
          group: ['../*'],
          message: 'Avoid relative imports from parent directories'
        }
      ]
    }],
    
    // 文件命名规范
    'unicorn/filename-case': ['error', {
      cases: {
        kebabCase: true,  // 文件名使用 kebab-case
        pascalCase: true  // React 组件可以用 PascalCase
      }
    }],
    
    // 强制使用 Node.js 前缀
    'unicorn/prefer-node-protocol': 'error'  // import fs from 'node:fs'
  }
}

2.2 Prettier 配置

2.2.1 Prettier 配置文件

javascript
// .prettierrc.js
module.exports = {
  // 基础格式
  printWidth: 80,
  tabWidth: 2,
  useTabs: false,
  semi: false,
  singleQuote: true,
  quoteProps: 'as-needed',
  jsxSingleQuote: false,
  
  // 尾随逗号
  trailingComma: 'es5',
  
  // 括号
  bracketSpacing: true,
  bracketSameLine: false,
  arrowParens: 'always',
  
  // 其他
  endOfLine: 'lf',
  embeddedLanguageFormatting: 'auto',
  singleAttributePerLine: false,
  
  // 覆盖规则
  overrides: [
    {
      files: '*.json',
      options: {
        printWidth: 200
      }
    },
    {
      files: '*.md',
      options: {
        proseWrap: 'always'
      }
    }
  ]
}
# .prettierignore
dist/
coverage/
.next/
.turbo/
*.min.js
*.d.ts
pnpm-lock.yaml
CHANGELOG.md

2.2.2 ESLint 与 Prettier 集成

javascript
// .eslintrc.js
module.exports = {
  extends: [
    // 其他配置...
    'prettier'  // 禁用与 Prettier 冲突的规则
  ],
  plugins: [
    'prettier'
  ],
  rules: {
    'prettier/prettier': ['error', {}, {
      usePrettierrc: true  // 使用 .prettierrc 的配置
    }]
  }
}

2.3 lint-staged 配置

2.3.1 基础配置

javascript
// .lintstagedrc.js
module.exports = {
  // TypeScript 文件
  '*.{ts,tsx}': [
    'eslint --fix',
    'prettier --write',
    () => 'tsc --noEmit'  // 类型检查
  ],
  
  // JavaScript 文件
  '*.{js,jsx}': [
    'eslint --fix',
    'prettier --write'
  ],
  
  // 样式文件
  '*.{css,scss,less}': [
    'stylelint --fix',
    'prettier --write'
  ],
  
  // JSON 文件
  '*.json': [
    'prettier --write'
  ],
  
  // Markdown 文件
  '*.md': [
    'prettier --write',
    'markdownlint --fix'
  ]
}

2.3.2 高级配置

javascript
// .lintstagedrc.advanced.js
const micromatch = require('micromatch')

module.exports = {
  // 动态命令生成
  '*.ts': async (files) => {
    // 过滤测试文件
    const testFiles = micromatch(files, ['**/*.test.ts', '**/*.spec.ts'])
    const sourceFiles = files.filter(f => !testFiles.includes(f))
    
    const commands = []
    
    if (sourceFiles.length > 0) {
      commands.push(`eslint --fix ${sourceFiles.join(' ')}`)
      commands.push(`prettier --write ${sourceFiles.join(' ')}`)
    }
    
    if (testFiles.length > 0) {
      commands.push(`jest --findRelatedTests ${testFiles.join(' ')}`)
    }
    
    // 类型检查(针对整个项目)
    commands.push('tsc --noEmit')
    
    return commands
  },
  
  // 包大小检查
  'package.json': () => [
    'size-limit'
  ]
}

IDE 配置

3.1 VS Code 设置

3.1.1 工作区设置

json
// .vscode/settings.json
{
  // TypeScript
  "typescript.tsdk": "./node_modules/typescript/lib",
  "typescript.enablePromptUseWorkspaceTsdk": true,
  "typescript.tsserver.maxTsServerMemory": 4096,
  "typescript.preferences.includePackageJsonAutoImports": "on",
  "typescript.preferences.importModuleSpecifier": "shortest",
  "typescript.updateImportsOnFileMove.enabled": "always",
  
  // 编辑器
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "editor.formatOnSave": true,
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": "explicit",
    "source.organizeImports": "never"  // ESLint 处理 import 排序
  },
  
  // ESLint
  "eslint.validate": [
    "javascript",
    "javascriptreact",
    "typescript",
    "typescriptreact"
  ],
  "eslint.workingDirectories": [
    { "mode": "auto" }
  ],
  
  // 文件排除
  "files.exclude": {
    "**/.git": true,
    "**/.DS_Store": true,
    "**/node_modules": true,
    "**/dist": true,
    "**/.turbo": true
  },
  
  // 搜索排除
  "search.exclude": {
    "**/node_modules": true,
    "**/dist": true,
    "**/*.tsbuildinfo": true,
    "**/coverage": true
  }
}

3.1.2 推荐扩展

json
// .vscode/extensions.json
{
  "recommendations": [
    // TypeScript 核心
    "ms-vscode.vscode-typescript-next",
    
    // 代码质量
    "dbaeumer.vscode-eslint",
    "esbenp.prettier-vscode",
    "stylelint.vscode-stylelint",
    
    // 开发体验
    "christian-kohler.path-intellisense",
    "formulahendry.auto-rename-tag",
    "streetsidesoftware.code-spell-checker",
    
    // Git
    "eamodio.gitlens",
    "mhutchie.git-graph",
    
    // 调试
    "ms-vscode.js-debug-nightly",
    
    // 测试
    "orta.vscode-jest",
    "firsttris.vscode-jest-runner",
    
    // 其他
    "yoavbls.pretty-ts-errors",
    "usernamehw.errorlens",
    "aaron-bond.better-comments"
  ]
}

3.2 WebStorm 设置

3.2.1 代码风格配置

xml
<!-- .idea/codeStyles/Project.xml -->
<component name="ProjectCodeStyleConfiguration">
  <code_scheme name="Project" version="173">
    <TypeScriptCodeStyleSettings version="0">
      <option name="USE_SEMICOLON_AFTER_STATEMENT" value="false" />
      <option name="FORCE_SEMICOLON_STYLE" value="true" />
      <option name="FILE_NAME_STYLE" value="KEBAB_CASE" />
      <option name="USE_DOUBLE_QUOTES" value="false" />
      <option name="FORCE_QUOTE_STYLE" value="true" />
      <option name="SPACES_WITHIN_IMPORTS" value="true" />
    </TypeScriptCodeStyleSettings>
  </code_scheme>
</component>

3.2.2 TypeScript 配置

xml
<!-- .idea/typescript-compiler.xml -->
<project version="4">
  <component name="TypeScriptCompiler">
    <option name="typeScriptServiceDirectory" value="$PROJECT_DIR$/node_modules/typescript/lib" />
    <option name="versionType" value="SERVICE_DIRECTORY" />
    <option name="nodeInterpreterTextField" value="$PROJECT_DIR$/node_modules/.bin/node" />
  </component>
</project>

3.3 必备插件推荐

3.3.1 VS Code 插件配置

json
// 插件的用户设置示例
{
  // Error Lens
  "errorLens.enabledDiagnosticLevels": ["warning", "error"],
  "errorLens.excludeBySource": ["eslint(prettier/prettier)"],
  
  // Better Comments
  "better-comments.tags": [
    {
      "tag": "!",
      "color": "#FF2D00",
      "strikethrough": false,
      "backgroundColor": "transparent"
    },
    {
      "tag": "?",
      "color": "#3498DB",
      "strikethrough": false,
      "backgroundColor": "transparent"
    },
    {
      "tag": "//",
      "color": "#474747",
      "strikethrough": true,
      "backgroundColor": "transparent"
    },
    {
      "tag": "todo",
      "color": "#FF8C00",
      "strikethrough": false,
      "backgroundColor": "transparent"
    },
    {
      "tag": "*",
      "color": "#98C379",
      "strikethrough": false,
      "backgroundColor": "transparent"
    }
  ],
  
  // GitLens
  "gitlens.codeLens.enabled": false,  // 减少视觉干扰
  "gitlens.currentLine.enabled": false
}

TypeScript 构建配置

4.1 构建工具选择(优先简单易用)

4.1.1 推荐工具优先级

typescript
// 🥇 开发首选:tsx(零配置,即用即走)
// 用途:开发环境运行、脚本执行、测试运行
// 优势:无需任何配置,启动速度极快
npm install -D tsx
tsx src/index.ts  // 直接运行

// 🥈 构建首选:tsup(一行配置搞定)
// 用途:打包库、CLI 工具、生产构建
// 优势:基于 esbuild,快速且能生成 .d.ts
npm install -D tsup
tsup src/index.ts  // 自动输出多种格式

// 🥉 前端应用:Vite(开发体验最佳)
// 用途:React/Vue/Svelte 应用
// 优势:HMR 极快,生态完善
npm create vite@latest

// ⚠️ 仅在必要时使用
// tsc - 仅用于类型检查和生成纯声明文件
// webpack - 仅在需要复杂配置时使用
// rollup - 仅在需要极致优化库体积时使用

4.2 简单配置示例

4.2.1 tsup 零配置使用

bash
# 最简单:零配置直接使用
tsup src/index.ts

# 指定输出格式
tsup src/index.ts --format cjs,esm

# 生成类型声明
tsup src/index.ts --dts

# Watch 模式
tsup src/index.ts --watch
json
// package.json - 最简配置
{
  "scripts": {
    "build": "tsup",  // 默认会找 src/index.ts
    "build:watch": "tsup --watch"
  }
}

4.2.2 tsup.config.ts(只在需要时创建)

typescript
// tsup.config.ts - 只包含必要配置
import { defineConfig } from 'tsup'

export default defineConfig({
  entry: ['src/index.ts'],
  format: ['cjs', 'esm'],
  dts: true,  // 生成 .d.ts
  clean: true,  // 清理 dist 目录
  // 其他保持默认即可!
})

4.2.3 最简单的构建流程

json
// package.json - 最简单的完整流程
{
  "scripts": {
    // 🎯 核心三件套
    "dev": "tsx watch src/index.ts",     // 开发:tsx
    "build": "tsup",                      // 构建:tsup
    "type-check": "tsc --noEmit",        // 类型检查:tsc
    
    // 其他常用脚本
    "test": "tsx --test src/**/*.test.ts",
    "lint": "eslint . --fix",
    "format": "prettier --write ."
  },
  
  // tsup 默认会读取这些字段
  "main": "./dist/index.js",
  "module": "./dist/index.mjs",
  "types": "./dist/index.d.ts",
  "exports": {
    ".": {
      "require": "./dist/index.js",
      "import": "./dist/index.mjs",
      "types": "./dist/index.d.ts"
    }
  }

4.3 与 Monorepo 集成

4.3.1 工具选择策略

json
// Monorepo 中不同类型包的工具选择

// 🏗️ 库包(packages/utils, packages/ui)
{
  "scripts": {
    "dev": "tsup --watch",  // 需要构建产物供其他包使用
    "build": "tsup"
  }
}

// 🚀 应用包(apps/web, apps/admin)  
{
  "scripts": {
    "dev": "tsx watch src/index.ts",  // 直接运行,无需构建
    "build": "tsup"
  }
}

// 🛠️ 工具包(tools/scripts, tools/cli)
{
  "scripts": {
    "dev": "tsx src/index.ts",  // 脚本直接执行
    "start": "tsx src/index.ts"
    // 通常不需要 build
  }
}

// 📦 根目录统一调度
{
  "scripts": {
    "dev": "turbo run dev",  // Turbo 会并行运行所有包的 dev
    "build": "turbo run build"
  }
}

关于构建优化的详细配置,参考 Monorepo 工程化规范

4.3.2 TypeScript 特定的缓存配置

json
// turbo.json - TypeScript 特定任务
{
  "pipeline": {
    "type-check": {
      "inputs": [
        "**/*.ts",
        "**/*.tsx",
        "tsconfig.json",
        "tsconfig.*.json"
      ],
      "outputs": [
        ".tsbuildinfo"
      ],
      "cache": true
    },
    
    "build:types": {
      "dependsOn": ["^build:types"],
      "inputs": [
        "src/**/*.ts",
        "src/**/*.tsx",
        "!src/**/*.test.ts",
        "!src/**/*.spec.ts",
        "tsconfig.build.json"
      ],
      "outputs": [
        "dist/**/*.d.ts",
        "dist/**/*.d.ts.map"
      ],
      "cache": true
    }
  }
}

常见问题

tsx 运行报错?

bash
# 常见问题:路径别名不识别
# 解决方案:tsx 会自动读取 tsconfig.json 的 paths
# 确保 tsconfig.json 中有正确的配置
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"]
    }
  }
}

# 常见问题:环境变量不加载
# 解决方案:使用 --env-file 参数
tsx --env-file=.env src/index.ts

# 常见问题:内存不足
# 解决方案:增加 Node.js 内存
NODE_OPTIONS='--max-old-space-size=4096' tsx src/index.ts

tsx vs ts-node?

typescript
// tsx 优势:
// ✅ 速度快 10-100x(基于 esbuild)
// ✅ 零配置,开箱即用
// ✅ 原生支持 ESM 和 CommonJS
// ✅ 自动处理 .ts/.tsx/.mts/.cts

// ts-node 场景:
// - 需要类型检查时(tsx 不检查类型)
// - 需要特定的 TypeScript 转换器
// - 旧项目兼容性

// 迁移建议:
// package.json
{
  "scripts": {
    // 旧:ts-node
    "dev:old": "ts-node src/index.ts",
    // 新:tsx
    "dev": "tsx src/index.ts"
  }
}

ESLint 性能慢?

javascript
// .eslintrc.js - 性能优化
module.exports = {
  parserOptions: {
    project: './tsconfig.eslint.json',  // 使用精简的 tsconfig
    tsconfigRootDir: __dirname,
    ecmaVersion: 2022,
    sourceType: 'module',
    // 性能优化
    EXPERIMENTAL_useProjectService: true  // 实验性功能
  },
  
  // 避免昂贵的规则
  rules: {
    // 这些规则需要类型信息,比较慢
    '@typescript-eslint/no-floating-promises': 'off',
    '@typescript-eslint/no-misused-promises': 'off'
  }
}

Prettier 与 ESLint 冲突?

bash
# 安装冲突解决包
npm install -D eslint-config-prettier

# 检查冲突
npx eslint-config-prettier .eslintrc.js

TypeScript 内存溢出?

json
// .vscode/settings.json
{
  "typescript.tsserver.maxTsServerMemory": 8192  // 增加内存限制
}

// package.json
{
  "scripts": {
    "type-check": "node --max-old-space-size=8192 ./node_modules/.bin/tsc"
  }
}

最佳实践

DO ✅

  • 优先使用 tsx 进行开发,零配置最重要
  • 使用 tsup 进行构建,一行命令搞定
  • 保持简单:能用默认配置就不要自定义
  • 配置 ESLint 和 Prettier 协同工作
  • 使用 lint-staged 进行渐进式代码质量改进
  • 类型检查与构建分离(tsc 只管类型,构建用其他工具)

DON'T ❌

  • 不要过度配置,大部分项目默认配置就够了
  • 不要在开发环境用 tsc 编译(用 tsx 更快)
  • 避免 ts-node,除非有特殊需求
  • 不要禁用 skipLibCheck(影响性能)
  • 避免过度的 ESLint 规则(影响开发体验)
  • 不要忽略工具的默认配置(通常是最佳实践)

下一步

完成工具链配置后:

  • 运行 npm run type-check 验证类型配置
  • 运行 npm run lint 检查代码规范
  • 在 IDE 中测试自动补全和类型提示
  • 参考 Monorepo 规范 了解更大规模的项目组织

基于 MIT 许可发布