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()
  • 数组高阶方法 :forEach、map、filter、reduce、every、some、indexOf
  • 对象方法 :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/await、Object.values/entries、字符串填充
ES2018 2018 ES9 异步迭代、对象展开/剩余、正则增强
ES2019 2019 ES10 Array.flat/flatMap、Object.fromEntries、可选 catch 绑定
ES2020 2020 ES11 可选链 ?.、空值合并 ??、BigInt、globalThis、Promise.allSettled
ES2021 2021 ES12 String.replaceAll、逻辑赋值运算符、Promise.any、WeakRef
ES2022 2022 ES13 顶层 await、类私有字段/方法、.at() 方法、Object.hasOwn
ES2023 2023 ES14 findLast/findLastIndex、不可变数组方法(toSorted 等)、Hashbang
ES2024 2024 ES15 Object.groupBy、Promise.withResolvers、ArrayBuffer.resize、正则 v 标志
ES2025 2025 ES16 Set 新方法(union 等)、Iterator 辅助方法、import 属性、Temporal(引擎渐进支持)

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

2.1 变量声明:let 与 const

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

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 映射类型、条件类型、keyof、infer
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: true 和 strict: 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 的不可变数组方法(toSorted、toReversed、toSpliced)
  • 使用 Object.freeze() 冻结常量对象

10.2 TypeScript 最佳实践

类型系统

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

接口与类型

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

泛型

  • 仅在类型需要被使用者决定时使用泛型
  • 为泛型添加有意义的名称(如 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(极端场景)
  • 利用 WeakRef 和 WeakMap 管理缓存,避免内存泄漏
  • 合理使用 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> 首字母小写

相关推荐
FYKJ_20102 小时前
springboot刑事案件管理系统03047-计算机课程设计、毕业设计
vue.js·spring boot·python·mysql·typescript·spark·django
怕浪猫2 小时前
分享一个做视频的skill,这条白板视频,每一笔都是代码画的
前端·javascript·面试
OpsEye6 小时前
当企业不再只关心能不能调用模型,而是关心值不值得调用
javascript·ai编程
用户0934077735146 小时前
HarmonyOS WPS Open SDK 实践:从 HAR 集成到 OpenFileRequest 最小闭环
typescript·harmonyos
FYKJ_20106 小时前
springboot雅集社区养老管理系统04456-计算机课程设计、毕业设计
vue.js·spring boot·python·mysql·typescript·spark·django
FYKJ_20106 小时前
express皖美特色农产品网售系统53118-计算机课程设计、毕业设计
javascript·vue.js·spring boot·mysql·typescript·spark·express
FYKJ_20107 小时前
springboot羽毛球场地管理系统00626-计算机课程设计、毕业设计
vue.js·spring boot·python·mysql·typescript·spark·django
晓得迷路了7 小时前
栗子前端技术周刊第 148 期 - Turborepo 2.11、Chrome 154 iframe、Node.js 26...
前端·javascript·css
FYKJ_20107 小时前
django药饮个性化推荐系统54839-计算机课程设计、毕业设计
vue.js·spring boot·python·mysql·typescript·spark·django
默_笙12 小时前
🚓 分诊台与拆题术:让 RAG 学会判断和规划
前端·javascript