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 的推荐步骤:
- 添加 tsconfig.json :设置
allowJs: true和strict: false,让 JS 和 TS 共存 - 逐文件重命名 :从依赖最少的工具模块开始,将
.js改为.ts - 添加类型注解:优先为函数参数和返回值添加类型
- 安装 @types 包 :
npm install -D @types/node @types/express等 - 逐步启用严格选项 :先开
noImplicitAny,再开strictNullChecks,最后全面strict: true - 使用 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> |
首字母小写 |