JavaScript与TypeScript终极指南

JavaScript(ES6+)与 TypeScript 完整知识总结与使用教程

本文档全面涵盖 JavaScript(ES6+)和 TypeScript 的发展历程、核心语法、高级特性、最佳实践与工程化方案,适合从入门到精通的各阶段开发者。


目录

  • [第一部分:JavaScript 发展历程](#第一部分:JavaScript 发展历程)
  • 第二部分:ES6(ES2015)核心特性
  • [第三部分:ES2016 --- ES2025 逐版新特性](#第三部分:ES2016 — ES2025 逐版新特性)
  • [第四部分:JavaScript 高级专题](#第四部分:JavaScript 高级专题)
  • [第五部分:TypeScript 发展历程](#第五部分:TypeScript 发展历程)
  • [第六部分:TypeScript 核心语法](#第六部分:TypeScript 核心语法)
  • [第七部分:TypeScript 高级类型系统](#第七部分:TypeScript 高级类型系统)
  • [第八部分:TypeScript 工程化实践](#第八部分:TypeScript 工程化实践)
  • [第九部分:JavaScript 与 TypeScript 互操作](#第九部分:JavaScript 与 TypeScript 互操作)
  • 第十部分:最佳实践与编码规范

第一部分:JavaScript 发展历程

1.1 起源与早期(1995---1999)

JavaScript 由 Brendan Eich 于 1995 年在 Netscape 公司用大约 10 天时间创造,最初命名为 Mocha,后改名 LiveScript,最终因与 Sun Microsystems(Java 的创造者)的市场合作而更名为 JavaScript。

1996 年,微软推出 JScript 作为 IE 浏览器中的竞争实现。为了统一标准,Netscape 将 JavaScript 提交给 ECMA 国际标准化组织,由此诞生了 ECMAScript 这一官方规范名称。

版本 年份 要点
ES1 1997 第一版标准化规范发布
ES2 1998 编辑性修订,与 ISO/IEC 16262 对齐
ES3 1999 正则表达式、try/catch、更完善的字符串处理

1.2 停滞与分裂(2000---2008)

ES4 的开发过程充满争议。以 Mozilla 和 Adobe 为代表的一方希望大幅革新语言(引入类、模块、静态类型等),而以微软和 Yahoo 为代表的另一方认为改动过大。最终 ES4 被废弃,从未正式发布。这一时期 JavaScript 的标准化实际上陷入了长达十年的停滞。

但与此同时,Ajax 技术的兴起(2005 年 Google Maps、Gmail 等应用)让 JavaScript 重新受到关注,jQuery(2006)等库极大提升了开发体验。

1.3 复兴:ES5(2009)

ES5 是 ES3 之后的第一个重大更新,奠定了"现代 JavaScript"的基础:

  • 严格模式"use strict"):消除部分不安全行为
  • JSON 支持JSON.parse() / JSON.stringify()
  • 数组高阶方法forEachmapfilterreduceeverysomeindexOf
  • 对象方法Object.keys()Object.defineProperty()Object.create()
  • 属性访问器:getter / setter
  • Function.prototype.bind()

1.4 革命性飞跃:ES6/ES2015

ES6(2015 年 6 月发布)是 JavaScript 历史上最大规模的一次更新,引入了几十项新特性,彻底改变了 JavaScript 的编码方式。从 ES2016 开始,TC39 委员会采用年度发布节奏,每年 6 月发布一个新版本,确保语言持续迭代而不再出现长时间空窗。

1.5 年度迭代时代(2016---至今)

版本 年份 代号 核心特性概览
ES2016 2016 ES7 Array.prototype.includes、指数运算符 **
ES2017 2017 ES8 async/awaitObject.values/entries、字符串填充
ES2018 2018 ES9 异步迭代、对象展开/剩余、正则增强
ES2019 2019 ES10 Array.flat/flatMapObject.fromEntries、可选 catch 绑定
ES2020 2020 ES11 可选链 ?.、空值合并 ??BigIntglobalThisPromise.allSettled
ES2021 2021 ES12 String.replaceAll、逻辑赋值运算符、Promise.anyWeakRef
ES2022 2022 ES13 顶层 await、类私有字段/方法、.at() 方法、Object.hasOwn
ES2023 2023 ES14 findLast/findLastIndex、不可变数组方法(toSorted 等)、Hashbang
ES2024 2024 ES15 Object.groupByPromise.withResolversArrayBuffer.resize、正则 v 标志
ES2025 2025 ES16 Set 新方法(union 等)、Iterator 辅助方法、import 属性、Temporal(引擎渐进支持)

第二部分:ES6(ES2015)核心特性

2.1 变量声明:let 与 const

ES6 之前只有 var,它是函数作用域的,且存在变量提升,容易产生隐蔽 bug。letconst 引入了块级作用域。

javascript 复制代码
// var 的问题 ------ 变量提升 + 函数作用域
function varProblem() {
  for (var i = 0; i < 3; i++) {
    setTimeout(() => console.log(i), 100); // 输出 3, 3, 3
  }
}

// let 的解决 ------ 块级作用域
function letSolution() {
  for (let i = 0; i < 3; i++) {
    setTimeout(() => console.log(i), 100); // 输出 0, 1, 2
  }
}

// const ------ 声明常量(绑定不可变,值可变)
const API_URL = 'https://api.example.com';
// API_URL = 'other'; // TypeError

const arr = [1, 2, 3];
arr.push(4); // 允许 ------ 修改的是值,不是绑定

使用原则 :默认使用 const,只在需要重新赋值时使用 let,永远不使用 var

2.2 箭头函数

javascript 复制代码
// 传统函数
const add = function(a, b) { return a + b; };

// 箭头函数
const add = (a, b) => a + b;

// 单参数可省略括号
const double = x => x * 2;

// 返回对象字面量需加括号
const makeUser = (name, age) => ({ name, age });

// 箭头函数没有自己的 this,继承外层作用域
const timer = {
  seconds: 0,
  start() {
    setInterval(() => {
      this.seconds++; // this 指向 timer 对象
    }, 1000);
  }
};

何时不用箭头函数

  • 对象方法(需要自己的 this
  • 构造函数(箭头函数不能用 new
  • 需要 arguments 对象时
  • 需要动态 this 时(如事件处理器绑定到 DOM 元素)

2.3 模板字符串

javascript 复制代码
const name = '世界';
const greeting = `你好,${name}!`;

// 多行字符串
const html = `
  <div class="card">
    <h2>${title}</h2>
    <p>${content}</p>
  </div>
`;

// 标签模板(Tagged Templates)
function highlight(strings, ...values) {
  return strings.reduce((result, str, i) => {
    return result + str + (values[i] ? `<mark>${values[i]}</mark>` : '');
  }, '');
}
const msg = highlight`欢迎 ${username},您有 ${count} 条新消息`;

2.4 解构赋值

javascript 复制代码
// 数组解构
const [first, second, ...rest] = [1, 2, 3, 4, 5];
// first=1, second=2, rest=[3,4,5]

// 跳过元素
const [, , third] = [1, 2, 3]; // third=3

// 默认值
const [a = 10, b = 20] = [1]; // a=1, b=20

// 交换变量
let x = 1, y = 2;
[x, y] = [y, x]; // x=2, y=1

// 对象解构
const { name, age, city = '未知' } = { name: '张三', age: 25 };

// 重命名
const { name: userName, age: userAge } = user;

// 嵌套解构
const { address: { street, zipCode } } = person;

// 函数参数解构
function createUser({ name, age, role = 'user' }) {
  return { name, age, role };
}

2.5 展开运算符与剩余参数

javascript 复制代码
// 展开数组
const arr1 = [1, 2, 3];
const arr2 = [...arr1, 4, 5]; // [1,2,3,4,5]

// 展开对象(浅拷贝/合并)
const defaults = { theme: 'dark', lang: 'zh' };
const userConfig = { lang: 'en' };
const config = { ...defaults, ...userConfig };
// { theme: 'dark', lang: 'en' }

// 剩余参数
function sum(...numbers) {
  return numbers.reduce((total, n) => total + n, 0);
}
sum(1, 2, 3, 4); // 10

2.6 默认参数

javascript 复制代码
function greet(name = '访客', greeting = `你好,${name}`) {
  console.log(greeting);
}
greet();         // "你好,访客"
greet('小明');   // "你好,小明"

2.7 类(Class)

javascript 复制代码
class Animal {
  // 构造函数
  constructor(name) {
    this.name = name;
  }

  // 实例方法
  speak() {
    console.log(`${this.name} 发出声音`);
  }

  // 静态方法
  static create(name) {
    return new Animal(name);
  }

  // getter / setter
  get info() {
    return `动物: ${this.name}`;
  }
}

// 继承
class Dog extends Animal {
  constructor(name, breed) {
    super(name); // 调用父类构造函数
    this.breed = breed;
  }

  speak() {
    console.log(`${this.name} 汪汪叫`);
  }
}

const dog = new Dog('旺财', '柴犬');
dog.speak(); // "旺财 汪汪叫"

2.8 Promise

javascript 复制代码
// 创建 Promise
const fetchData = (url) => {
  return new Promise((resolve, reject) => {
    const success = true;
    if (success) {
      resolve({ data: '数据内容' });
    } else {
      reject(new Error('请求失败'));
    }
  });
};

// 使用 Promise
fetchData('/api/users')
  .then(result => console.log(result))
  .catch(error => console.error(error))
  .finally(() => console.log('请求结束'));

// Promise 组合
Promise.all([fetch(url1), fetch(url2)])       // 全部成功才成功
  .then(([res1, res2]) => { /* ... */ });

Promise.race([fetch(url1), fetch(url2)])      // 取最快完成的
  .then(result => { /* ... */ });

2.9 模块系统(ES Modules)

javascript 复制代码
// math.js ------ 命名导出
export const PI = 3.14159;
export function add(a, b) { return a + b; }

// utils.js ------ 默认导出
export default class Logger {
  log(msg) { console.log(msg); }
}

// main.js ------ 导入
import Logger from './utils.js';            // 默认导入
import { PI, add } from './math.js';        // 命名导入
import { add as sum } from './math.js';     // 重命名导入
import * as MathUtils from './math.js';     // 命名空间导入

2.10 Symbol

javascript 复制代码
// 创建唯一标识符
const id = Symbol('id');
const user = { [id]: 123, name: '张三' };

// Symbol 不会出现在 for...in 和 Object.keys 中
console.log(Object.keys(user)); // ['name']

// 全局 Symbol 注册表
const globalSym = Symbol.for('app.id');
Symbol.for('app.id') === globalSym; // true

// 内置 Symbol:自定义对象行为
class Range {
  constructor(start, end) {
    this.start = start;
    this.end = end;
  }
  [Symbol.iterator]() {
    let current = this.start;
    const end = this.end;
    return {
      next() {
        return current <= end
          ? { value: current++, done: false }
          : { done: true };
      }
    };
  }
}
for (const n of new Range(1, 5)) console.log(n); // 1,2,3,4,5

2.11 迭代器与 for...of

javascript 复制代码
// 任何实现了 [Symbol.iterator] 的对象都是可迭代的
// 内置可迭代对象:Array, String, Map, Set, TypedArray, arguments

for (const char of '你好') console.log(char); // "你", "好"

// 自定义迭代器(见上方 Range 示例)

2.12 生成器(Generator)

javascript 复制代码
function* fibonacci() {
  let [a, b] = [0, 1];
  while (true) {
    yield a;
    [a, b] = [b, a + b];
  }
}

const fib = fibonacci();
fib.next(); // { value: 0, done: false }
fib.next(); // { value: 1, done: false }
fib.next(); // { value: 1, done: false }
fib.next(); // { value: 2, done: false }

// 实际用途:惰性求值、无限序列、异步流程控制
function* idGenerator() {
  let id = 1;
  while (true) {
    yield id++;
  }
}

2.13 Map 与 Set

javascript 复制代码
// Map ------ 键值对集合,键可以是任意类型
const map = new Map();
map.set('name', '张三');
map.set(42, '数字键');
map.set(true, '布尔键');

map.get('name');  // '张三'
map.has(42);      // true
map.size;         // 3
map.delete(true);

for (const [key, value] of map) {
  console.log(`${key}: ${value}`);
}

// Set ------ 唯一值集合
const set = new Set([1, 2, 3, 2, 1]);
console.log(set.size); // 3
set.add(4);
set.has(3);   // true
set.delete(1);

// 数组去重
const unique = [...new Set([1, 1, 2, 3, 3])]; // [1, 2, 3]

// WeakMap / WeakSet ------ 键为弱引用,可被垃圾回收
const weakMap = new WeakMap();
let obj = {};
weakMap.set(obj, '元数据');
obj = null; // 键对象可被垃圾回收

2.14 Proxy 与 Reflect

javascript 复制代码
const handler = {
  get(target, prop, receiver) {
    console.log(`读取属性: ${prop}`);
    return Reflect.get(target, prop, receiver);
  },
  set(target, prop, value, receiver) {
    console.log(`设置属性: ${prop} = ${value}`);
    if (prop === 'age' && typeof value !== 'number') {
      throw new TypeError('age 必须是数字');
    }
    return Reflect.set(target, prop, value, receiver);
  }
};

const user = new Proxy({ name: '张三', age: 25 }, handler);
user.name;       // 控制台: "读取属性: name"
user.age = 30;   // 控制台: "设置属性: age = 30"
// user.age = '三十'; // TypeError

Proxy 的实际应用场景:数据验证、响应式系统(Vue 3 的核心原理)、属性访问日志、懒加载、默认值等。


第三部分:ES2016 --- ES2025 逐版新特性

3.1 ES2016(ES7)

javascript 复制代码
// Array.prototype.includes ------ 替代 indexOf
[1, 2, 3].includes(2);      // true
[1, 2, NaN].includes(NaN);  // true(indexOf 无法检测 NaN)

// 指数运算符
2 ** 10;  // 1024(等价于 Math.pow(2, 10))

3.2 ES2017(ES8)

javascript 复制代码
// async/await ------ 异步编程的革命性改进
async function fetchUser(id) {
  try {
    const response = await fetch(`/api/users/${id}`);
    const user = await response.json();
    return user;
  } catch (error) {
    console.error('获取用户失败:', error);
  }
}

// 并行执行多个异步操作
async function loadDashboard() {
  const [users, posts, comments] = await Promise.all([
    fetchUsers(),
    fetchPosts(),
    fetchComments()
  ]);
  render(users, posts, comments);
}

// Object.values / Object.entries
const obj = { a: 1, b: 2, c: 3 };
Object.values(obj);   // [1, 2, 3]
Object.entries(obj);  // [['a',1], ['b',2], ['c',3]]

// 字符串填充
'5'.padStart(3, '0');   // '005'
'hello'.padEnd(10, '.'); // 'hello.....'

// Object.getOwnPropertyDescriptors
// 用于精确复制对象(包括 getter/setter)

3.3 ES2018(ES9)

javascript 复制代码
// 异步迭代
async function* asyncRange(start, end) {
  for (let i = start; i <= end; i++) {
    await new Promise(r => setTimeout(r, 100));
    yield i;
  }
}

for await (const num of asyncRange(1, 5)) {
  console.log(num);
}

// 对象展开/剩余运算符(之前只支持数组)
const { x, y, ...remaining } = { x: 1, y: 2, a: 3, b: 4 };
// remaining = { a: 3, b: 4 }

// Promise.prototype.finally
fetch(url)
  .then(handleSuccess)
  .catch(handleError)
  .finally(() => hideLoadingSpinner());

// 正则表达式增强
// 命名捕获组
const dateRegex = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
const { groups: { year, month, day } } = dateRegex.exec('2025-06-15');

// 后行断言(Lookbehind)
/(?<=\$)\d+/.exec('$100'); // ['100']

// dotAll 模式(s 标志)
/foo.bar/s.test('foo\nbar'); // true

// Unicode 属性转义
/\p{Script=Han}/u.test('中'); // true ------ 匹配中文字符

3.4 ES2019(ES10)

javascript 复制代码
// Array.prototype.flat / flatMap
[1, [2, [3, [4]]]].flat(Infinity); // [1, 2, 3, 4]
[[1, 2], [3, 4]].flatMap(x => x);  // [1, 2, 3, 4]

// Object.fromEntries ------ Object.entries 的逆操作
const entries = [['name', '张三'], ['age', 25]];
Object.fromEntries(entries); // { name: '张三', age: 25 }

// 实际应用:Map 转对象、URLSearchParams 转对象
const params = new URLSearchParams('name=张三&age=25');
Object.fromEntries(params); // { name: '张三', age: '25' }

// String.prototype.trimStart / trimEnd
'  hello  '.trimStart(); // 'hello  '
'  hello  '.trimEnd();   // '  hello'

// 可选的 catch 绑定
try {
  JSON.parse(input);
} catch {  // 不需要 error 参数时可以省略
  console.log('解析失败');
}

// Symbol.prototype.description
const sym = Symbol('我的符号');
sym.description; // '我的符号'

3.5 ES2020(ES11)

javascript 复制代码
// 可选链操作符 ?.
const city = user?.address?.city;           // 属性访问
const result = arr?.[0];                     // 数组索引
const value = obj?.method?.();               // 方法调用

// 空值合并操作符 ??
const port = config.port ?? 3000;
// 仅在 null/undefined 时使用默认值
// 与 || 的区别:0、''、false 不会触发 ??

// BigInt ------ 任意精度整数
const big = 9007199254740991n;
const sum = big + 1n; // 9007199254740992n
// 注意:BigInt 不能与 Number 混合运算

// globalThis ------ 统一的全局对象引用
// 浏览器中等于 window,Node 中等于 global
console.log(globalThis);

// Promise.allSettled ------ 等待所有 Promise 完成(无论成功或失败)
const results = await Promise.allSettled([
  fetch('/api/a'),
  fetch('/api/b'),
  fetch('/api/c')
]);
results.forEach(result => {
  if (result.status === 'fulfilled') {
    console.log('成功:', result.value);
  } else {
    console.log('失败:', result.reason);
  }
});

// 动态 import()
const module = await import(`./modules/${moduleName}.js`);
module.default();

// String.prototype.matchAll
const text = 'test1 test2 test3';
const matches = [...text.matchAll(/test(\d)/g)];
// 每个 match 包含完整的匹配信息和捕获组

3.6 ES2021(ES12)

javascript 复制代码
// String.prototype.replaceAll
'aabbcc'.replaceAll('b', '_'); // 'aa__cc'

// 逻辑赋值运算符
let a = null;
a ??= 'default';  // a = 'default'(仅 null/undefined 时赋值)
a ||= 'fallback'; // 假值时赋值
a &&= 'update';   // 真值时赋值

// Promise.any ------ 取第一个成功的
const fastest = await Promise.any([
  fetch('https://cdn1.example.com/data'),
  fetch('https://cdn2.example.com/data'),
  fetch('https://cdn3.example.com/data')
]);

// 数字分隔符
const billion = 1_000_000_000;
const hex = 0xFF_EC_D5_9C;
const binary = 0b1010_0001;

// WeakRef ------ 弱引用
let target = { data: 'important' };
const ref = new WeakRef(target);
ref.deref(); // { data: 'important' } 或 undefined(已被回收时)

// FinalizationRegistry ------ 垃圾回收回调
const registry = new FinalizationRegistry(value => {
  console.log(`${value} 已被垃圾回收`);
});
registry.register(someObject, '对象标识');

3.7 ES2022(ES13)

javascript 复制代码
// 顶层 await ------ 在模块顶层直接使用 await
const config = await fetch('/config.json').then(r => r.json());
export default config;

// 类的新特性
class MyClass {
  // 公共实例字段
  name = '默认值';

  // 私有字段(# 前缀)
  #count = 0;

  // 私有方法
  #increment() {
    this.#count++;
  }

  // 静态字段
  static instances = 0;

  // 静态私有字段
  static #totalCount = 0;

  // 静态初始化块
  static {
    console.log('类初始化');
    MyClass.#totalCount = loadFromDB();
  }

  get count() {
    return this.#count;
  }
}

// in 操作符检查私有字段
class Brand {
  #secret;
  static isBrand(obj) {
    return #secret in obj;
  }
}

// Array/String/TypedArray.prototype.at()
const arr = [1, 2, 3, 4, 5];
arr.at(-1);  // 5(最后一个元素)
arr.at(-2);  // 4

'hello'.at(-1); // 'o'

// Object.hasOwn() ------ 替代 Object.prototype.hasOwnProperty.call()
Object.hasOwn({ a: 1 }, 'a'); // true

// Error.cause ------ 错误链
try {
  await fetch(url);
} catch (error) {
  throw new Error('网络请求失败', { cause: error });
}

// 正则 d 标志 ------ 获取匹配的索引位置
const match = /(\w+)/d.exec('hello world');
match.indices[0]; // [0, 5]

3.8 ES2023(ES14)

javascript 复制代码
// 从末尾查找
const arr = [1, 2, 3, 4, 5, 4, 3];
arr.findLast(x => x > 3);       // 4
arr.findLastIndex(x => x > 3);  // 5

// 不可变数组方法 ------ 返回新数组,不修改原数组
const sorted = arr.toSorted((a, b) => a - b);
const reversed = arr.toReversed();
const spliced = arr.toSpliced(1, 2, 'a', 'b');
const changed = arr.with(0, 'new'); // 替换指定索引的值

// Hashbang 语法支持
#!/usr/bin/env node
// 可以在脚本开头使用 #! 注释

// Symbol 作为 WeakMap 键
const weak = new WeakMap();
const key = Symbol('myKey');
weak.set(key, 'value');

3.9 ES2024(ES15)

javascript 复制代码
// Object.groupBy / Map.groupBy
const people = [
  { name: '张三', city: '北京' },
  { name: '李四', city: '上海' },
  { name: '王五', city: '北京' },
];

const byCity = Object.groupBy(people, p => p.city);
// { '北京': [{...}, {...}], '上海': [{...}] }

const byCityMap = Map.groupBy(people, p => p.city);
// Map { '北京' => [...], '上海' => [...] }

// Promise.withResolvers
const { promise, resolve, reject } = Promise.withResolvers();
setTimeout(() => resolve('完成'), 1000);
const result = await promise;

// ArrayBuffer 可调整大小
const buffer = new ArrayBuffer(8, { maxByteLength: 16 });
buffer.resize(12);

// 正则 v 标志(Unicode Sets)
// 支持集合操作:交集(&&)、差集(--)、嵌套字符类
/[\p{Script=Han}&&\p{Unified_Ideograph}]/v;

// Atomics.waitAsync ------ 非阻塞的原子等待
// String.prototype.isWellFormed / toWellFormed
'\uD800'.isWellFormed();    // false
'\uD800'.toWellFormed();    // '�'(替换为替换字符)

3.10 ES2025(ES16)

javascript 复制代码
// Set 新方法 ------ 集合运算
const setA = new Set([1, 2, 3, 4]);
const setB = new Set([3, 4, 5, 6]);

setA.union(setB);         // Set {1, 2, 3, 4, 5, 6}
setA.intersection(setB);  // Set {3, 4}
setA.difference(setB);    // Set {1, 2}
setA.symmetricDifference(setB); // Set {1, 2, 5, 6}
setA.isSubsetOf(setB);    // false
setA.isSupersetOf(setB);  // false
setA.isDisjointFrom(new Set([7, 8])); // true

// Iterator 辅助方法
function* naturals() {
  let n = 1;
  while (true) yield n++;
}

const result = naturals()
  .filter(n => n % 2 === 0)
  .map(n => n ** 2)
  .take(5)
  .toArray();
// [4, 16, 36, 64, 100]

// import 属性(Import Attributes)
import data from './config.json' with { type: 'json' };

const module = await import('./data.json', {
  with: { type: 'json' }
});

// RegExp.escape ------ 安全转义正则特殊字符
RegExp.escape('hello (world)'); // 'hello \\(world\\)'

// Float16Array ------ 半精度浮点数组(用于 AI/GPU 数据)
const f16 = new Float16Array([1.5, 2.5, 3.5]);

// Temporal API(各引擎逐步支持中)
// 全新的日期/时间 API,解决 Date 对象的众多问题
const now = Temporal.Now.plainDateTimeISO();
const date = Temporal.PlainDate.from({ year: 2025, month: 6, day: 15 });
const tomorrow = date.add({ days: 1 });
const duration = Temporal.Duration.from({ hours: 2, minutes: 30 });

第四部分:JavaScript 高级专题

4.1 闭包(Closure)

闭包是指函数能够"记住"并访问其定义时所在的词法作用域,即使函数在该作用域之外执行。

javascript 复制代码
function createCounter(initial = 0) {
  let count = initial;
  return {
    increment: () => ++count,
    decrement: () => --count,
    getCount: () => count,
  };
}

const counter = createCounter(10);
counter.increment(); // 11
counter.increment(); // 12
counter.getCount();  // 12
// count 变量被闭包"封装",外部无法直接访问

// 常见陷阱:循环中的闭包
for (var i = 0; i < 5; i++) {
  setTimeout(() => console.log(i), 100);
  // 全部输出 5,因为 var 是函数作用域
}
// 解决方案 1:使用 let
for (let i = 0; i < 5; i++) {
  setTimeout(() => console.log(i), 100); // 0,1,2,3,4
}
// 解决方案 2:IIFE
for (var i = 0; i < 5; i++) {
  ((j) => setTimeout(() => console.log(j), 100))(i);
}

4.2 原型与原型链

javascript 复制代码
// 每个对象都有一个内部属性 [[Prototype]]
const animal = {
  speak() { console.log('...'); }
};

const dog = Object.create(animal);
dog.bark = function() { console.log('汪!'); };

dog.bark();   // '汪!' ------ 自身属性
dog.speak();  // '...' ------ 沿原型链查找

// 原型链:dog -> animal -> Object.prototype -> null
console.log(Object.getPrototypeOf(dog) === animal); // true

// class 语法本质上是原型继承的语法糖
class Animal {
  speak() { console.log('...'); }
}
class Dog extends Animal {
  bark() { console.log('汪!'); }
}
// Dog.prototype.__proto__ === Animal.prototype

4.3 事件循环(Event Loop)

javascript 复制代码
// JavaScript 是单线程的,通过事件循环实现异步
// 执行顺序:同步代码 → 微任务(microtask) → 宏任务(macrotask)

console.log('1 - 同步');

setTimeout(() => console.log('2 - 宏任务(setTimeout)'), 0);

Promise.resolve().then(() => console.log('3 - 微任务(Promise)'));

queueMicrotask(() => console.log('4 - 微任务(queueMicrotask)'));

console.log('5 - 同步');

// 输出顺序:1, 5, 3, 4, 2

// 微任务:Promise.then/catch/finally, queueMicrotask, MutationObserver
// 宏任务:setTimeout, setInterval, setImmediate(Node), requestAnimationFrame, I/O

4.4 this 绑定规则

javascript 复制代码
// 1. 默认绑定(非严格模式下指向 globalThis)
function show() { console.log(this); }
show(); // window(浏览器)或 global(Node)

// 2. 隐式绑定(调用者对象)
const obj = { name: '张三', greet() { console.log(this.name); } };
obj.greet(); // '张三'

// 3. 显式绑定(call / apply / bind)
function greet(greeting) { console.log(`${greeting}, ${this.name}`); }
greet.call({ name: '李四' }, '你好');   // '你好, 李四'
greet.apply({ name: '王五' }, ['嗨']);   // '嗨, 王五'
const bound = greet.bind({ name: '赵六' });
bound('hello'); // 'hello, 赵六'

// 4. new 绑定(构造函数)
function Person(name) { this.name = name; }
const p = new Person('张三'); // this 指向新对象

// 5. 箭头函数(继承外层 this,不可被 call/apply/bind 改变)
const obj2 = {
  name: '张三',
  delayLog: function() {
    setTimeout(() => console.log(this.name), 100); // '张三'
  }
};

4.5 异步编程模式

javascript 复制代码
// 1. 串行执行
async function serial() {
  const a = await fetchA();
  const b = await fetchB(a);
  const c = await fetchC(b);
  return c;
}

// 2. 并行执行
async function parallel() {
  const [a, b, c] = await Promise.all([
    fetchA(), fetchB(), fetchC()
  ]);
  return { a, b, c };
}

// 3. 并发限制
async function parallelLimit(tasks, limit) {
  const results = [];
  const executing = new Set();

  for (const task of tasks) {
    const p = task().then(result => {
      executing.delete(p);
      return result;
    });
    executing.add(p);
    results.push(p);

    if (executing.size >= limit) {
      await Promise.race(executing);
    }
  }

  return Promise.all(results);
}

// 4. 重试机制
async function fetchWithRetry(url, retries = 3, delay = 1000) {
  for (let i = 0; i < retries; i++) {
    try {
      return await fetch(url);
    } catch (error) {
      if (i === retries - 1) throw error;
      await new Promise(r => setTimeout(r, delay * (i + 1)));
    }
  }
}

// 5. 异步迭代器实战
async function* paginate(url) {
  let page = 1;
  while (true) {
    const res = await fetch(`${url}?page=${page}`);
    const data = await res.json();
    if (data.items.length === 0) return;
    yield* data.items;
    page++;
  }
}

for await (const item of paginate('/api/posts')) {
  console.log(item);
}

4.6 模块化进阶

javascript 复制代码
// 动态导入 ------ 按需加载
const loadChart = async () => {
  const { Chart } = await import('chart.js');
  return new Chart(/* ... */);
};

// 条件导入
const adapter = await import(
  isNode ? './node-adapter.js' : './browser-adapter.js'
);

// 模块顶层 await(ES2022)
// config.js
const response = await fetch('/api/config');
export const config = await response.json();

// 导出模式
export { default as Button } from './Button.js';        // 重导出
export { default } from './MainComponent.js';            // 重导出默认
export * from './utils.js';                              // 重导出全部命名
export * as Utils from './utils.js';                     // 命名空间重导出

4.7 错误处理最佳实践

javascript 复制代码
// 自定义错误类
class AppError extends Error {
  constructor(message, code, cause) {
    super(message, { cause });
    this.name = 'AppError';
    this.code = code;
  }
}

class ValidationError extends AppError {
  constructor(field, message) {
    super(message, 'VALIDATION_ERROR');
    this.field = field;
  }
}

class NotFoundError extends AppError {
  constructor(resource) {
    super(`${resource} 不存在`, 'NOT_FOUND');
    this.resource = resource;
  }
}

// 全局错误处理
window.addEventListener('unhandledrejection', event => {
  console.error('未处理的 Promise 拒绝:', event.reason);
  event.preventDefault();
});

// 结构化错误处理
async function handleRequest(req) {
  try {
    const user = await findUser(req.id);
    if (!user) throw new NotFoundError('用户');
    return user;
  } catch (error) {
    if (error instanceof NotFoundError) {
      return { status: 404, message: error.message };
    }
    if (error instanceof ValidationError) {
      return { status: 400, message: error.message, field: error.field };
    }
    // 未知错误
    console.error('未知错误:', error);
    return { status: 500, message: '服务器内部错误' };
  }
}

第五部分:TypeScript 发展历程

5.1 诞生背景(2012)

随着 JavaScript 应用规模的膨胀,从几百行的浏览器脚本增长到数百万行的大型项目,JavaScript 缺乏类型系统的弱点愈发明显。微软内部的大型项目(如 Bing Maps、Office Online)深受其苦。

Anders Hejlsberg(C# 和 Delphi 的设计者)领导团队在微软内部开发了 TypeScript,于 2012 年 10 月首次公开发布(0.8 版本)。其核心理念是做 JavaScript 的超集------所有合法的 JavaScript 都是合法的 TypeScript,同时在此基础上添加可选的静态类型检查。

5.2 发展里程碑

时间 版本 重要事件
2012.10 0.8 首次公开发布
2014.04 1.0 第一个正式版本
2015 1.5-1.8 增加装饰器、async/await、模块系统改进
2016.09 2.0 严格空值检查、控制流分析、标签联合类型
2017 2.1-2.9 映射类型、条件类型、keyofinfer
2018.07 3.0 项目引用、元组改进
2019 3.5-3.7 Omit 工具类型、可选链/空值合并支持
2020.08 4.0 可变参元组类型、标签元组元素
2021 4.1-4.5 模板字面量类型、递归条件类型
2022 4.6-4.9 satisfies 运算符、using 声明
2023.03 5.0 装饰器标准化(Stage 3)、const 类型参数
2023-2024 5.1-5.7 --isolatedDeclarations、正则命名捕获类型推断
2025.03 5.8 Go 语言重写编译器的预览、性能大幅提升
2026(计划) 6.0 → 7.0 6.0 为旧编译器最后版本;7.0 使用 Go 重写的编译器,编译速度提升约 10 倍,strict 默认开启,移除 ES5 目标

5.3 生态地位(2026 年现状)

TypeScript 在 2024 年的 Stack Overflow 调查中位列全球第五大最常用语言(38.5%)。它已成为 GitHub 上使用最多的语言之一。Node.js 从 22.6.0 版本开始通过 type stripping 原生支持运行 TypeScript 文件(无需编译步骤),在 22.18.0 及以后版本默认启用。Google、Microsoft、Airbnb、Slack 等大厂全面采用 TypeScript。


第六部分:TypeScript 核心语法

6.1 基础类型

typescript 复制代码
// 原始类型
let isDone: boolean = false;
let count: number = 42;
let name: string = '张三';
let big: bigint = 100n;
let sym: symbol = Symbol('key');

// null 和 undefined
let u: undefined = undefined;
let n: null = null;

// 数组
let list: number[] = [1, 2, 3];
let list2: Array<number> = [1, 2, 3]; // 泛型写法

// 元组(Tuple)------ 固定长度和类型的数组
let pair: [string, number] = ['张三', 25];
let named: [name: string, age: number] = ['张三', 25]; // 标签元组

// 枚举
enum Direction {
  Up = 'UP',
  Down = 'DOWN',
  Left = 'LEFT',
  Right = 'RIGHT',
}

enum StatusCode {
  OK = 200,
  NotFound = 404,
  ServerError = 500,
}

// const 枚举 ------ 编译时内联,不生成运行时对象
const enum Color { Red, Green, Blue }
let c = Color.Red; // 编译为 let c = 0;

// any ------ 放弃类型检查(尽量避免使用)
let anything: any = 4;
anything = '字符串'; // 不报错

// unknown ------ 类型安全的 any
let value: unknown = getData();
// value.toString(); // 错误!必须先缩窄类型
if (typeof value === 'string') {
  value.toUpperCase(); // 正确
}

// void ------ 函数无返回值
function log(msg: string): void {
  console.log(msg);
}

// never ------ 永远不会有值的类型
function throwError(msg: string): never {
  throw new Error(msg);
}

function infiniteLoop(): never {
  while (true) {}
}

// object ------ 非原始类型
let obj: object = { key: 'value' };

6.2 接口(Interface)

typescript 复制代码
// 基本接口
interface User {
  readonly id: number;      // 只读属性
  name: string;
  age: number;
  email?: string;           // 可选属性
}

// 函数类型接口
interface SearchFunc {
  (source: string, subString: string): boolean;
}

// 索引签名
interface StringMap {
  [key: string]: string;
}

interface NumberArray {
  [index: number]: string;
}

// 接口继承
interface Person {
  name: string;
  age: number;
}

interface Employee extends Person {
  company: string;
  position: string;
}

// 多重继承
interface Manager extends Employee {
  department: string;
  subordinates: Employee[];
}

// 接口合并(Declaration Merging)
interface Config {
  host: string;
}
interface Config {
  port: number;
}
// 合并后等同于 interface Config { host: string; port: number; }

// 接口实现
class UserService implements User {
  readonly id: number;
  name: string;
  age: number;

  constructor(id: number, name: string, age: number) {
    this.id = id;
    this.name = name;
    this.age = age;
  }
}

6.3 类型别名(Type Alias)

typescript 复制代码
// 基本类型别名
type ID = string | number;
type Callback = (data: string) => void;
type Point = { x: number; y: number };

// 联合类型
type Status = 'pending' | 'active' | 'inactive';
type Result = Success | Failure;

// 交叉类型
type Admin = User & { permissions: string[] };

// 字面量类型
type Direction = 'north' | 'south' | 'east' | 'west';
type DiceRoll = 1 | 2 | 3 | 4 | 5 | 6;

// 模板字面量类型(TS 4.1+)
type EventName = `on${Capitalize<string>}`;
type CSSProperty = `${string}-${string}`;
type Locale = `${Language}-${Country}`;
type Language = 'zh' | 'en' | 'ja';
type Country = 'CN' | 'US' | 'JP';
// Locale = 'zh-CN' | 'zh-US' | 'zh-JP' | 'en-CN' | ... 共 9 种

interface vs type 选择指南

  • 优先使用 interface:描述对象形状、需要声明合并、需要 extends 继承时
  • 使用 type:联合类型、交叉类型、元组、映射类型、条件类型、基本类型别名

6.4 函数类型

typescript 复制代码
// 函数声明
function add(a: number, b: number): number {
  return a + b;
}

// 可选参数 & 默认参数
function greet(name: string, greeting: string = '你好'): string {
  return `${greeting}, ${name}`;
}

// 剩余参数
function sum(...numbers: number[]): number {
  return numbers.reduce((acc, n) => acc + n, 0);
}

// 函数重载
function createElement(tag: 'div'): HTMLDivElement;
function createElement(tag: 'span'): HTMLSpanElement;
function createElement(tag: 'input'): HTMLInputElement;
function createElement(tag: string): HTMLElement {
  return document.createElement(tag);
}

// 回调函数类型
type EventHandler = (event: MouseEvent) => void;

// this 参数(显式声明 this 的类型)
interface Card {
  suit: string;
  value: number;
}

function getCardInfo(this: Card): string {
  return `${this.suit} ${this.value}`;
}

6.5 泛型(Generics)

typescript 复制代码
// 泛型函数
function identity<T>(arg: T): T {
  return arg;
}
identity<string>('hello');
identity(42); // 类型推断为 number

// 泛型接口
interface Repository<T> {
  getById(id: string): Promise<T>;
  getAll(): Promise<T[]>;
  create(item: T): Promise<T>;
  update(id: string, item: Partial<T>): Promise<T>;
  delete(id: string): Promise<void>;
}

// 泛型类
class Stack<T> {
  private items: T[] = [];

  push(item: T): void {
    this.items.push(item);
  }

  pop(): T | undefined {
    return this.items.pop();
  }

  peek(): T | undefined {
    return this.items[this.items.length - 1];
  }

  get size(): number {
    return this.items.length;
  }
}

// 泛型约束
interface HasLength {
  length: number;
}

function logLength<T extends HasLength>(arg: T): T {
  console.log(arg.length);
  return arg;
}
logLength('hello');    // 正确,string 有 length
logLength([1, 2, 3]); // 正确,array 有 length
// logLength(42);      // 错误,number 没有 length

// keyof 约束
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

const user = { name: '张三', age: 25 };
getProperty(user, 'name'); // 类型为 string
// getProperty(user, 'email'); // 错误!

// 多个类型参数
function merge<T, U>(obj1: T, obj2: U): T & U {
  return { ...obj1, ...obj2 };
}

// 泛型默认值
interface PaginatedResult<T = any> {
  items: T[];
  total: number;
  page: number;
  pageSize: number;
}

// const 类型参数(TS 5.0+)------ 推断字面量类型
function createConfig<const T extends Record<string, unknown>>(config: T): T {
  return config;
}
const cfg = createConfig({ port: 3000, host: 'localhost' });
// 类型为 { readonly port: 3000; readonly host: "localhost" }

6.6 类型缩窄(Type Narrowing)

typescript 复制代码
// typeof 缩窄
function padLeft(value: string, padding: string | number): string {
  if (typeof padding === 'number') {
    return ' '.repeat(padding) + value;
  }
  return padding + value;
}

// instanceof 缩窄
function handleError(error: Error | string) {
  if (error instanceof Error) {
    console.log(error.message);
    console.log(error.stack);
  } else {
    console.log(error);
  }
}

// in 操作符缩窄
interface Fish { swim(): void; }
interface Bird { fly(): void; }

function move(animal: Fish | Bird) {
  if ('swim' in animal) {
    animal.swim();
  } else {
    animal.fly();
  }
}

// 可辨识联合(Discriminated Unions)
interface Circle { kind: 'circle'; radius: number; }
interface Square { kind: 'square'; side: number; }
interface Triangle { kind: 'triangle'; base: number; height: number; }
type Shape = Circle | Square | Triangle;

function getArea(shape: Shape): number {
  switch (shape.kind) {
    case 'circle':
      return Math.PI * shape.radius ** 2;
    case 'square':
      return shape.side ** 2;
    case 'triangle':
      return 0.5 * shape.base * shape.height;
    default:
      const _exhaustive: never = shape; // 穷尽检查
      return _exhaustive;
  }
}

// 自定义类型守卫(Type Guard)
interface Admin {
  role: 'admin';
  permissions: string[];
}
interface User {
  role: 'user';
  email: string;
}

function isAdmin(person: Admin | User): person is Admin {
  return person.role === 'admin';
}

function handle(person: Admin | User) {
  if (isAdmin(person)) {
    console.log(person.permissions); // TypeScript 知道这是 Admin
  }
}

// 断言函数(Assertion Function)
function assertIsString(val: unknown): asserts val is string {
  if (typeof val !== 'string') {
    throw new Error('不是字符串!');
  }
}

function processInput(input: unknown) {
  assertIsString(input);
  console.log(input.toUpperCase()); // 此处 input 已缩窄为 string
}

6.7 TypeScript 中的类

typescript 复制代码
class Animal {
  // 访问修饰符
  public name: string;      // 默认,任何地方可访问
  protected sound: string;  // 本类及子类可访问
  private _age: number;     // 仅本类可访问

  // 简写构造函数(参数属性)
  constructor(
    public readonly id: number,
    name: string,
    sound: string,
    age: number
  ) {
    this.name = name;
    this.sound = sound;
    this._age = age;
  }

  // getter / setter
  get age(): number {
    return this._age;
  }

  set age(value: number) {
    if (value < 0) throw new Error('年龄不能为负');
    this._age = value;
  }

  // 方法
  speak(): string {
    return `${this.name}: ${this.sound}`;
  }
}

// 抽象类
abstract class Shape {
  abstract getArea(): number;
  abstract getPerimeter(): number;

  // 抽象类可以有具体实现
  describe(): string {
    return `面积: ${this.getArea()}, 周长: ${this.getPerimeter()}`;
  }
}

class Circle extends Shape {
  constructor(private radius: number) {
    super();
  }

  getArea(): number {
    return Math.PI * this.radius ** 2;
  }

  getPerimeter(): number {
    return 2 * Math.PI * this.radius;
  }
}

// 接口实现
interface Serializable {
  serialize(): string;
  deserialize(data: string): void;
}

interface Printable {
  print(): void;
}

class Document implements Serializable, Printable {
  constructor(private content: string) {}

  serialize(): string {
    return JSON.stringify({ content: this.content });
  }

  deserialize(data: string): void {
    this.content = JSON.parse(data).content;
  }

  print(): void {
    console.log(this.content);
  }
}

6.8 装饰器(Decorators)

TypeScript 5.0 起支持标准化的 Stage 3 装饰器。

typescript 复制代码
// 类装饰器
function sealed(constructor: Function) {
  Object.seal(constructor);
  Object.seal(constructor.prototype);
}

// 方法装饰器
function log(
  target: any,
  propertyKey: string,
  descriptor: PropertyDescriptor
) {
  const original = descriptor.value;
  descriptor.value = function (...args: any[]) {
    console.log(`调用 ${propertyKey},参数:`, args);
    const result = original.apply(this, args);
    console.log(`${propertyKey} 返回:`, result);
    return result;
  };
}

@sealed
class Calculator {
  @log
  add(a: number, b: number): number {
    return a + b;
  }
}

// TS 5.0 标准装饰器(Stage 3)
function logMethod<This, Args extends any[], Return>(
  target: (this: This, ...args: Args) => Return,
  context: ClassMethodDecoratorContext<This, (this: This, ...args: Args) => Return>
) {
  return function (this: This, ...args: Args): Return {
    console.log(`调用 ${String(context.name)}`);
    return target.call(this, ...args);
  };
}

class MyService {
  @logMethod
  getData(id: number): string {
    return `data-${id}`;
  }
}

第七部分:TypeScript 高级类型系统

7.1 内置工具类型(Utility Types)

typescript 复制代码
interface User {
  id: number;
  name: string;
  email: string;
  age: number;
  role: 'admin' | 'user';
}

// Partial<T> ------ 所有属性变为可选
type UpdateUser = Partial<User>;
// { id?: number; name?: string; email?: string; ... }

// Required<T> ------ 所有属性变为必需
type StrictUser = Required<User>;

// Readonly<T> ------ 所有属性变为只读
type FrozenUser = Readonly<User>;

// Pick<T, K> ------ 选取部分属性
type UserProfile = Pick<User, 'name' | 'email' | 'age'>;

// Omit<T, K> ------ 排除部分属性
type CreateUserDTO = Omit<User, 'id'>;

// Record<K, V> ------ 构造键值对类型
type UserRoles = Record<string, 'admin' | 'user' | 'guest'>;
type PageViews = Record<'home' | 'about' | 'contact', number>;

// Exclude<T, U> ------ 从联合类型中排除
type NonNullableString = Exclude<string | null | undefined, null | undefined>;
// string

// Extract<T, U> ------ 从联合类型中提取
type NumberOrString = Extract<string | number | boolean, string | number>;
// string | number

// NonNullable<T> ------ 排除 null 和 undefined
type SafeString = NonNullable<string | null | undefined>; // string

// ReturnType<T> ------ 获取函数返回类型
function fetchUser() { return { name: '张三', age: 25 }; }
type UserReturn = ReturnType<typeof fetchUser>;
// { name: string; age: number }

// Parameters<T> ------ 获取函数参数类型元组
type FetchParams = Parameters<typeof fetch>;
// [input: RequestInfo | URL, init?: RequestInit]

// ConstructorParameters<T> ------ 获取构造函数参数类型
type DateParams = ConstructorParameters<typeof Date>;

// InstanceType<T> ------ 获取构造函数的实例类型
type RegExpInstance = InstanceType<typeof RegExp>;

// Awaited<T> ------ 解包 Promise 类型
type Data = Awaited<Promise<Promise<string>>>; // string

7.2 映射类型(Mapped Types)

typescript 复制代码
// 基本映射类型
type Getters<T> = {
  [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};

interface Person {
  name: string;
  age: number;
}

type PersonGetters = Getters<Person>;
// { getName: () => string; getAge: () => number; }

// 条件映射
type ReadonlyExcept<T, K extends keyof T> = {
  readonly [P in keyof T as P extends K ? never : P]: T[P];
} & {
  [P in K]: T[P];
};

// 移除特定属性修饰符
type Mutable<T> = {
  -readonly [K in keyof T]: T[K];
};

type Optional<T> = {
  [K in keyof T]+?: T[K];
};

// 键重映射(Key Remapping, TS 4.1+)
type EventHandlers<T> = {
  [K in keyof T as `on${Capitalize<string & K>}Change`]: (value: T[K]) => void;
};

type UserHandlers = EventHandlers<User>;
// { onNameChange: (value: string) => void; onAgeChange: (value: number) => void; ... }

7.3 条件类型(Conditional Types)

typescript 复制代码
// 基本条件类型
type IsString<T> = T extends string ? true : false;

type A = IsString<string>;  // true
type B = IsString<number>;  // false

// 分布式条件类型(Distributive Conditional Types)
type ToArray<T> = T extends unknown ? T[] : never;
type Result = ToArray<string | number>;
// string[] | number[](分布在联合的每个成员上)

// infer 关键字 ------ 在条件类型中推断类型
type UnpackPromise<T> = T extends Promise<infer U> ? U : T;
type Data = UnpackPromise<Promise<string>>; // string

type ArrayElement<T> = T extends (infer E)[] ? E : T;
type Elem = ArrayElement<number[]>; // number

// 获取函数第一个参数的类型
type FirstArg<T> = T extends (first: infer F, ...rest: any[]) => any ? F : never;
type First = FirstArg<(name: string, age: number) => void>; // string

// 递归条件类型(TS 4.1+)
type DeepReadonly<T> = T extends object
  ? { readonly [K in keyof T]: DeepReadonly<T[K]> }
  : T;

// 实用案例:类型安全的事件系统
type EventMap = {
  click: { x: number; y: number };
  focus: { target: HTMLElement };
  input: { value: string };
};

type EventHandler<E extends keyof EventMap> = (payload: EventMap[E]) => void;

class EventEmitter {
  on<E extends keyof EventMap>(event: E, handler: EventHandler<E>): void {
    // ...
  }
  emit<E extends keyof EventMap>(event: E, payload: EventMap[E]): void {
    // ...
  }
}

7.4 模板字面量类型

typescript 复制代码
// 基本模板字面量类型
type Greeting = `Hello, ${string}`;
let g: Greeting = 'Hello, World'; // 正确
// let g2: Greeting = 'Hi, World'; // 错误

// 与联合类型结合
type Color = 'red' | 'blue' | 'green';
type Size = 'small' | 'medium' | 'large';
type ClassNames = `${Size}-${Color}`;
// "small-red" | "small-blue" | "small-green" | "medium-red" | ...

// 内置字符串操作类型
type Upper = Uppercase<'hello'>;       // "HELLO"
type Lower = Lowercase<'HELLO'>;       // "hello"
type Cap = Capitalize<'hello'>;        // "Hello"
type Uncap = Uncapitalize<'Hello'>;    // "hello"

// 实用模式:从字符串推断类型
type ParseRoute<S extends string> =
  S extends `${infer Start}/:${infer Param}/${infer Rest}`
    ? { param: Param } & ParseRoute<`/${Rest}`>
    : S extends `${infer Start}/:${infer Param}`
    ? { param: Param }
    : {};

type Route = ParseRoute<'/users/:userId/posts/:postId'>;
// { param: "userId" } & { param: "postId" }

7.5 satisfies 运算符(TS 4.9+)

typescript 复制代码
// satisfies 检查类型兼容性,但保留更窄的推断类型
type Colors = Record<string, [number, number, number] | string>;

const palette = {
  red: [255, 0, 0],
  green: '#00ff00',
  blue: [0, 0, 255],
} satisfies Colors;

// palette.red 的类型是 [number, number, number](不是 string | [...])
palette.red.map(x => x / 2); // 正确!

// 对比 as 断言
const palette2: Colors = {
  red: [255, 0, 0],
  green: '#00ff00',
  blue: [0, 0, 255],
};
// palette2.red 的类型是 string | [number, number, number]
// palette2.red.map(...) // 错误!需要类型缩窄

7.6 自定义工具类型实战

typescript 复制代码
// DeepPartial ------ 深层可选
type DeepPartial<T> = T extends object
  ? { [K in keyof T]?: DeepPartial<T[K]> }
  : T;

// RequireAtLeastOne ------ 至少需要一个属性
type RequireAtLeastOne<T, Keys extends keyof T = keyof T> =
  Pick<T, Exclude<keyof T, Keys>> &
  { [K in Keys]-?: Required<Pick<T, K>> & Partial<Pick<T, Exclude<Keys, K>>> }[Keys];

// Prettify ------ 展开交叉类型,使 hover 更可读
type Prettify<T> = {
  [K in keyof T]: T[K];
} & {};

// StrictOmit ------ 严格的 Omit(键必须存在于类型中)
type StrictOmit<T, K extends keyof T> = Omit<T, K>;

// PathKeys ------ 获取对象所有路径
type PathKeys<T, Prefix extends string = ''> = T extends object
  ? {
      [K in keyof T & string]: T[K] extends object
        ? `${Prefix}${K}` | PathKeys<T[K], `${Prefix}${K}.`>
        : `${Prefix}${K}`;
    }[keyof T & string]
  : never;

interface Config {
  db: { host: string; port: number };
  cache: { ttl: number };
}
type ConfigPaths = PathKeys<Config>;
// "db" | "db.host" | "db.port" | "cache" | "cache.ttl"

第八部分:TypeScript 工程化实践

8.1 tsconfig.json 详解

jsonc 复制代码
{
  "compilerOptions": {
    // ===== 目标与模块 =====
    "target": "ES2022",           // 编译目标版本
    "module": "ESNext",           // 模块系统
    "moduleResolution": "bundler", // 模块解析策略(推荐 bundler/node16)
    "lib": ["ES2023", "DOM", "DOM.Iterable"],  // 引入的类型库

    // ===== 严格模式(强烈推荐全部开启) =====
    "strict": true,               // 开启所有严格检查,等同于以下全部为 true:
    // "noImplicitAny": true,        // 禁止隐式 any
    // "strictNullChecks": true,     // 严格空值检查
    // "strictFunctionTypes": true,  // 严格函数类型检查
    // "strictBindCallApply": true,  // 严格 bind/call/apply 检查
    // "strictPropertyInitialization": true, // 类属性必须初始化
    // "noImplicitThis": true,       // 禁止隐式 this
    // "alwaysStrict": true,         // 始终以严格模式解析

    // ===== 额外检查 =====
    "noUnusedLocals": true,          // 未使用的局部变量报错
    "noUnusedParameters": true,      // 未使用的参数报错
    "noImplicitReturns": true,       // 所有分支必须有返回值
    "noFallthroughCasesInSwitch": true, // switch 禁止 fallthrough
    "noUncheckedIndexedAccess": true,   // 索引访问可能为 undefined
    "exactOptionalPropertyTypes": true, // 精确可选属性类型

    // ===== 输出 =====
    "outDir": "./dist",
    "rootDir": "./src",
    "declaration": true,             // 生成 .d.ts 声明文件
    "declarationMap": true,          // 声明文件的 sourcemap
    "sourceMap": true,               // 生成 sourcemap
    "removeComments": true,          // 移除注释

    // ===== 互操作 =====
    "esModuleInterop": true,         // 允许 import X from 'cjs-module'
    "allowSyntheticDefaultImports": true,
    "forceConsistentCasingInFileNames": true, // 文件名大小写敏感
    "resolveJsonModule": true,       // 允许导入 JSON
    "isolatedModules": true,         // 确保每个文件可独立编译

    // ===== 路径别名 =====
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"],
      "@components/*": ["src/components/*"],
      "@utils/*": ["src/utils/*"]
    },

    // ===== JSX =====
    "jsx": "react-jsx",  // React 17+ 的 JSX 转换

    // ===== 增量编译 =====
    "incremental": true,
    "tsBuildInfoFile": "./.tsbuildinfo",

    // ===== 项目引用 =====
    "composite": true     // 启用项目引用
  },

  "include": ["src/**/*.ts", "src/**/*.tsx"],
  "exclude": ["node_modules", "dist", "**/*.test.ts"]
}

8.2 声明文件(.d.ts)

typescript 复制代码
// 为没有类型的第三方库编写声明文件
// types/my-lib.d.ts
declare module 'my-lib' {
  export function doSomething(input: string): Promise<Result>;
  export interface Result {
    success: boolean;
    data: unknown;
  }
  export default class MyLib {
    constructor(config: Config);
    run(): void;
  }
  interface Config {
    apiKey: string;
    debug?: boolean;
  }
}

// 全局类型声明
// types/global.d.ts
declare global {
  interface Window {
    __APP_CONFIG__: {
      apiUrl: string;
      version: string;
    };
  }

  // 扩展现有类型
  interface Array<T> {
    customMethod(): T[];
  }
}
export {}; // 使文件成为模块

// 环境声明
declare const __DEV__: boolean;
declare const __VERSION__: string;

// 模块声明(处理非 JS 资源导入)
declare module '*.css' {
  const classes: Record<string, string>;
  export default classes;
}
declare module '*.svg' {
  const content: string;
  export default content;
}
declare module '*.png' {
  const src: string;
  export default src;
}

8.3 项目引用(Project References)

大型单仓库(monorepo)项目中,项目引用可以将代码拆分为多个子项目以提升编译速度。

jsonc 复制代码
// tsconfig.json(根目录)
{
  "files": [],
  "references": [
    { "path": "./packages/core" },
    { "path": "./packages/api" },
    { "path": "./packages/web" }
  ]
}

// packages/core/tsconfig.json
{
  "compilerOptions": {
    "composite": true,
    "outDir": "./dist",
    "rootDir": "./src"
  },
  "include": ["src/**/*"]
}

// packages/api/tsconfig.json
{
  "compilerOptions": {
    "composite": true,
    "outDir": "./dist"
  },
  "references": [
    { "path": "../core" }  // 依赖 core 子项目
  ]
}

使用 tsc --build(简写 tsc -b)可以根据依赖关系增量编译各子项目。

8.4 TypeScript 与 React

tsx 复制代码
import { useState, useEffect, useCallback, useRef, FC, ReactNode } from 'react';

// Props 类型定义
interface ButtonProps {
  variant: 'primary' | 'secondary' | 'danger';
  size?: 'small' | 'medium' | 'large';
  disabled?: boolean;
  children: ReactNode;
  onClick: (event: React.MouseEvent<HTMLButtonElement>) => void;
}

// 函数组件
const Button: FC<ButtonProps> = ({
  variant,
  size = 'medium',
  disabled = false,
  children,
  onClick,
}) => {
  return (
    <button
      className={`btn btn-${variant} btn-${size}`}
      disabled={disabled}
      onClick={onClick}
    >
      {children}
    </button>
  );
};

// 泛型组件
interface ListProps<T> {
  items: T[];
  renderItem: (item: T, index: number) => ReactNode;
  keyExtractor: (item: T) => string;
}

function List<T>({ items, renderItem, keyExtractor }: ListProps<T>) {
  return (
    <ul>
      {items.map((item, index) => (
        <li key={keyExtractor(item)}>{renderItem(item, index)}</li>
      ))}
    </ul>
  );
}

// Hooks 的类型化
function useLocalStorage<T>(key: string, initialValue: T) {
  const [value, setValue] = useState<T>(() => {
    const stored = localStorage.getItem(key);
    return stored ? JSON.parse(stored) : initialValue;
  });

  useEffect(() => {
    localStorage.setItem(key, JSON.stringify(value));
  }, [key, value]);

  return [value, setValue] as const;
}

// useRef 的类型化
function TextInput() {
  const inputRef = useRef<HTMLInputElement>(null);

  const focus = useCallback(() => {
    inputRef.current?.focus();
  }, []);

  return <input ref={inputRef} onBlur={focus} />;
}

// 事件处理
function Form() {
  const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    const formData = new FormData(e.currentTarget);
  };

  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    console.log(e.target.value);
  };

  return (
    <form onSubmit={handleSubmit}>
      <input onChange={handleChange} />
    </form>
  );
}

8.5 TypeScript 与 Node.js

typescript 复制代码
// Node.js 22.6+ 原生支持 TypeScript(Type Stripping)
// 直接运行:node app.ts

// Express 应用示例
import express, { Request, Response, NextFunction } from 'express';

interface CreateUserBody {
  name: string;
  email: string;
  age: number;
}

interface UserParams {
  id: string;
}

const app = express();
app.use(express.json());

// 类型化的路由处理
app.post('/users', (
  req: Request<{}, {}, CreateUserBody>,
  res: Response,
  next: NextFunction
) => {
  const { name, email, age } = req.body;
  // name, email, age 都有正确的类型
  res.json({ id: '1', name, email, age });
});

app.get('/users/:id', (
  req: Request<UserParams>,
  res: Response
) => {
  const { id } = req.params; // id: string
  res.json({ id });
});

// 错误处理中间件
interface AppError extends Error {
  statusCode: number;
  code: string;
}

app.use((err: AppError, req: Request, res: Response, next: NextFunction) => {
  res.status(err.statusCode || 500).json({
    error: err.message,
    code: err.code,
  });
});

第九部分:JavaScript 与 TypeScript 互操作

9.1 渐进式迁移策略

从 JavaScript 迁移到 TypeScript 的推荐步骤:

  1. 添加 tsconfig.json :设置 allowJs: truestrict: false,让 JS 和 TS 共存
  2. 逐文件重命名 :从依赖最少的工具模块开始,将 .js 改为 .ts
  3. 添加类型注解:优先为函数参数和返回值添加类型
  4. 安装 @types 包npm install -D @types/node @types/express
  5. 逐步启用严格选项 :先开 noImplicitAny,再开 strictNullChecks,最后全面 strict: true
  6. 使用 JSDoc 为 JS 文件添加类型(过渡阶段):
javascript 复制代码
// utils.js ------ 通过 JSDoc 获得类型检查(配合 checkJs: true)
/**
 * @param {string} name
 * @param {number} age
 * @returns {{ name: string, age: number, greet: () => string }}
 */
function createUser(name, age) {
  return {
    name,
    age,
    greet() {
      return `你好,我是${name}`;
    },
  };
}

/** @type {import('./types').Config} */
const config = loadConfig();

9.2 类型安全的运行时验证

TypeScript 的类型只在编译时存在,运行时会被擦除。对于外部数据(API 响应、用户输入等),需要运行时验证。

typescript 复制代码
// 使用 Zod 进行运行时验证并自动推导类型
import { z } from 'zod';

const UserSchema = z.object({
  id: z.number(),
  name: z.string().min(1).max(100),
  email: z.string().email(),
  age: z.number().int().min(0).max(150),
  role: z.enum(['admin', 'user', 'guest']),
  tags: z.array(z.string()).optional(),
});

// 从 Schema 推导 TypeScript 类型
type User = z.infer<typeof UserSchema>;
// { id: number; name: string; email: string; age: number; role: 'admin' | 'user' | 'guest'; tags?: string[] }

// 安全解析
function parseUser(data: unknown): User {
  return UserSchema.parse(data); // 验证失败会抛出 ZodError
}

// 安全解析(不抛错)
const result = UserSchema.safeParse(data);
if (result.success) {
  console.log(result.data); // 类型为 User
} else {
  console.error(result.error.issues);
}

第十部分:最佳实践与编码规范

10.1 JavaScript 最佳实践

变量与作用域

  • 使用 const 为默认,需要重新赋值时用 let,永远不用 var
  • 避免全局变量污染,使用模块系统

函数

  • 遵循单一职责原则,每个函数只做一件事
  • 使用默认参数代替条件判断
  • 优先使用纯函数(无副作用)
  • 复杂逻辑提前返回(Guard Clause)
javascript 复制代码
// 差
function processUser(user) {
  if (user) {
    if (user.isActive) {
      if (user.hasPermission) {
        // 处理逻辑
      }
    }
  }
}

// 好
function processUser(user) {
  if (!user) return;
  if (!user.isActive) return;
  if (!user.hasPermission) return;
  // 处理逻辑
}

异步编程

  • 优先使用 async/await 而非 .then()
  • 使用 Promise.all() 并行化无依赖的异步操作
  • 始终处理错误(try/catch 或 .catch())
  • 避免在循环中串行 await(改用 Promise.all + map

不可变性

  • 使用展开运算符创建新对象/数组而非修改原始值
  • 使用 ES2023 的不可变数组方法(toSortedtoReversedtoSpliced
  • 使用 Object.freeze() 冻结常量对象

10.2 TypeScript 最佳实践

类型系统

  • 避免使用 any;需要"任意类型"时使用 unknown
  • 优先让 TypeScript 推断类型,仅在必要时添加显式注解
  • 使用可辨识联合(Discriminated Union)代替 as 类型断言
  • 开启 strict: true 并保持所有严格选项

接口与类型

  • 对象形状优先用 interface,联合/交叉/元组用 type
  • 使用 satisfies 代替类型断言来验证对象类型
  • 利用工具类型(PartialPickOmit 等)减少重复

泛型

  • 仅在类型需要被使用者决定时使用泛型
  • 为泛型添加有意义的名称(如 TItem 而非 T)或合理的约束
  • 避免过度泛型化导致类型难以理解

项目组织

  • 类型定义集中放在 types/ 目录或与使用者同级的 .types.ts 文件中
  • 导出类型时使用 export type 以便 bundler 优化
  • 使用 import type 仅导入类型
typescript 复制代码
// 明确区分类型导入和值导入
import type { User, Config } from './types';
import { createUser, validateConfig } from './utils';

// 导出类型
export type { User, Config };

10.3 性能优化要点

JavaScript 性能

  • 使用 Map / Set 替代频繁查找的对象/数组
  • 大数据集使用 for 循环而非 .forEach/.map(极端场景)
  • 利用 WeakRefWeakMap 管理缓存,避免内存泄漏
  • 合理使用 Web Worker 处理 CPU 密集任务
  • 使用 requestAnimationFrame 优化动画

TypeScript 编译性能

  • 启用 incremental 增量编译
  • 大型项目使用项目引用拆分编译单元
  • 使用 skipLibCheck: true 跳过第三方库的类型检查
  • 减少复杂的条件类型和递归类型的嵌套深度
  • TypeScript 7.0(预计 2026 年中)将采用 Go 重写的编译器,编译速度预计提升约 10 倍

10.4 工具链推荐(2026)

用途 推荐工具
构建工具 Vite、esbuild、Turbopack
代码检查 ESLint + typescript-eslint
格式化 Prettier、Biome
测试 Vitest、Jest + ts-jest
包管理 pnpm(推荐)、npm、yarn
运行时 Node.js 22+(原生 TS)、Deno、Bun
类型检查 tsc --noEmit(配合 bundler 编译)
API 验证 Zod、Valibot
Monorepo Turborepo、Nx

附录 A:ECMAScript 提案阶段说明

TC39(技术委员会第 39 号)负责 ECMAScript 标准的制定,每个新特性都需经历以下阶段:

阶段 名称 含义
Stage 0 Strawperson 初步想法,任何 TC39 成员都可以提出
Stage 1 Proposal 正式提案,有确定的推进者和初步方案
Stage 2 Draft 草案,语法和语义基本确定
Stage 2.7 --- 规范文本完成,等待实现验证
Stage 3 Candidate 候选,需要至少两个引擎实现
Stage 4 Finished 完成,纳入下一版 ECMAScript 标准

附录 B:常用速查表

B.1 数组方法速查

方法 是否修改原数组 返回值 ES 版本
push/pop 新长度/被移除元素 ES3
shift/unshift 被移除元素/新长度 ES3
splice 被移除的元素数组 ES3
sort 排序后的数组 ES3
reverse 反转后的数组 ES3
fill 填充后的数组 ES6
copyWithin 修改后的数组 ES6
map 新数组 ES5
filter 新数组 ES5
reduce 累积值 ES5
find 找到的元素或 undefined ES6
findIndex 索引或 -1 ES6
includes boolean ES2016
flat 新数组 ES2019
flatMap 新数组 ES2019
at 对应元素 ES2022
findLast 找到的元素或 undefined ES2023
toSorted 新排序数组 ES2023
toReversed 新反转数组 ES2023
toSpliced 新数组 ES2023
with 新数组 ES2023

B.2 TypeScript 工具类型速查

工具类型 作用
Partial<T> 所有属性变可选
Required<T> 所有属性变必需
Readonly<T> 所有属性变只读
Pick<T, K> 选取部分属性
Omit<T, K> 排除部分属性
Record<K, V> 构造键值对类型
Exclude<T, U> 从联合中排除
Extract<T, U> 从联合中提取
NonNullable<T> 排除 null/undefined
ReturnType<T> 函数返回类型
Parameters<T> 函数参数类型元组
InstanceType<T> 构造函数实例类型
Awaited<T> 解包 Promise
Uppercase<S> 字符串大写
Lowercase<S> 字符串小写
Capitalize<S> 首字母大写
Uncapitalize<S> 首字母小写

相关推荐
叫我一声阿雷吧2 小时前
JS 入门通关手册(45):浏览器渲染原理与重绘重排(性能优化核心,面试必考
javascript·前端面试·前端性能优化·浏览器渲染·浏览器渲染原理,重排重绘·reflow·repaint
大家的林语冰2 小时前
《前端周刊》尤大开源 Vite+ 全家桶,前端工业革命启动;尤大爆料 Void 云服务新产品,Vite 进军全栈开发;ECMA 源码映射规范......
前端·javascript·vue.js
数据知道3 小时前
claw-code 源码分析:从 TypeScript 心智到 Python/Rust——跨栈移植时类型、边界与错误模型怎么对齐?
python·ai·rust·typescript·claude code·claw code
jiayong233 小时前
第 8 课:开始引入组合式函数
前端·javascript·学习
12345,catch a tiger3 小时前
虚拟机ubuntu安装Vmware Tools
linux·运维·ubuntu
天若有情6734 小时前
【C++原创开源】formort.h:一行头文件,实现比JS模板字符串更爽的链式拼接+响应式变量
开发语言·javascript·c++·git·github·开源项目·模版字符串
辰风沐阳4 小时前
OpenClaw 安装教程(Ubuntu 24.04 Desktop)
linux·ubuntu
yuki_uix4 小时前
重排、重绘与合成——浏览器渲染性能的底层逻辑
前端·javascript·面试
止观止5 小时前
拥抱 ESNext:从 TC39 提案到生产环境中的现代 JS
开发语言·javascript·ecmascript·esnext