本工具使用的开源库

本工具代码中捆绑了 1 个开源库。

TypeScript 速查 — 简明参考

TypeScript 5 语法、类型系统与最常用惯用法速查手册,覆盖约 80% 日常场景。

TS

TypeScript TypeScript 5.x

ECMAScript + 类型 · 多范式 · 结构化 · 静态(渐进)类型于 JS 之上

学习路径

先搭好编译环境(tsc/tsconfig)并理解 TS 是 JS 的超集 → 掌握基础类型标注、联合类型与接口 → 深入类型系统(泛型、类型守卫、类型操作)→ 用 class 与模块组织代码 → 处理异步与 DOM/Node 类型 → 最后按需查构建配置、测试与调试。FAQ 节适合回头避坑。

1.Hello World 与构建环境

编译运行 TypeScript,理解 tsc、tsconfig 与类型检查流程。

最小程序

TS 是 JS 超集:合法 JS 就是合法 TS。加类型标注后经 tsc 编译为 JS。

1
2
3
4
5
6
// hello.ts
const message: string = 'Hello, world!';
console.log(message);
// 类型标注:变量名后的 : string
// $ npx tsc hello.ts # 编译生成 hello.js
// $ node hello.js

tsc 编译

tsc 编译 .ts 为 .js。--noEmit 只做类型检查不输出、--watch 监听改动、--strict 严格模式。

1
2
3
4
5
// $ npx tsc app.ts # 编译单文件
// $ npx tsc --noEmit # 只检查类型
// $ npx tsc --watch # 监听自动编译
// $ npx tsc --strict # 严格类型检查
// $ npx tsc --outDir dist # 输出目录

tsconfig.json

tsconfig.json 配置编译:target、module、strict、outDir。npx tsc 自动读取。

1
2
3
4
5
6
7
8
9
10
11
12
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true, // 严格模式
"outDir": "dist",
"sourceMap": true // 调试用 source map
},
"include": ["src"]
}
// 用 tsc --init 生成默认配置

直接运行 TS

用 tsx/ts-node 直接运行 TS 无需先编译。Node 22 原生支持 --experimental-strip-types。

1
2
3
4
5
// $ npx tsx script.ts # 直接运行 TS
// $ npx ts-node script.ts # 老方案
// Node 22+:
// $ node --experimental-strip-types script.ts
// 开发脚本常用 tsx,生产 tsc 编译后运行

严格模式

strict: true 开启全部严格检查:null 检查、隐式 any 报错、未使用变量警告。新项目必须开。

1
2
3
4
5
6
7
8
// strict 包含:
// 1. strictNullChecks null 不能赋给普通类型
// 2. noImplicitAny 参数无类型时报错
// 3. noUnusedLocals 未使用变量报错
function greet(name: string) { // 显式类型
return 'Hi ' + name;
}
// 不开 strict 时 name 隐式 any 不报错

TS 与 JS 的关系

TS 在编译期检查类型,编译后 JS 无类型信息。类型只在编译期存在,运行时被擦除。

1
2
3
4
5
6
7
8
interface User {
name: string;
age: number;
}
const u: User = { name: 'Nick', age: 30 };
// 编译产物(类型被擦除):
// const u = { name: 'Nick', age: 30 };
// 类型错误在编译期捕获,不影响运行

依赖与类型包

库需类型定义。@types/* 是 DefinitelyTyped 社区类型包。typescript 是编译工具依赖。

1
2
3
4
5
6
// $ npm i -D typescript @types/node
// $ npm i -D @types/express # 社区类型
// 库自带类型:
// axios 直接 export 类型,无需 @types
// // @ts-ignore 注释:跳过下一行检查(慎用)
// import axios from 'axios'; 有类型定义

编辑器集成

VS Code 内置 TS 支持:悬停看类型、报错红波浪线、自动补全、重构。错误信息直接显示。

1
2
3
4
5
6
7
// VS Code 快捷键:
// 悬停:查看类型
// 快速修复:Ctrl+. (Cmd+.)
// 重命名符号:F2
// 跳到定义:F12
// 类型错误出现在 Problems 面板
// 配置:使用工作区 tsconfig 需要 `tsc` 版本一致

2.变量与类型标注

类型标注、类型推断、联合类型、any/unknown 与类型断言。

类型标注

变量名后 : 类型 标注类型。标注后类型固定,赋其他类型报编译错误。

1
2
3
4
5
6
const name: string = 'Nick';
let age: number = 30;
const isAdmin: boolean = true;
// 错误示例:
// age = 'thirty'; // 类型错误
// 初始化后类型推断,可不显式标注

类型推断

TS 从初始化值推断类型,多数情况无需显式标注。复杂类型推荐显式接口。

1
2
3
4
5
6
7
8
const name = 'Nick'; // 推断为 string
let count = 42; // number
const arr = [1, 2, 3]; // number[]
const obj = { a: 1 }; // { a: number }
// 赋值时推断联合:
let status = 'idle' as const;
// 复杂场景显式标注更清晰:
let data: Map<string, User> = new Map();

联合类型

| 联合类型表示「或」:string | number 两者皆可。访问成员需先类型守卫。

1
2
3
4
5
6
7
8
9
10
11
12
function print(value: string | number) {
// 联合类型只能访问公共成员
console.log(value.toString());
// 分支处理:
if (typeof value === 'string') {
console.log(value.toUpperCase()); // string
} else {
console.log(value.toFixed(2)); // number
}
}
// 字面量联合:
type Dir = 'up' | 'down' | 'left' | 'right';

any 与 unknown

any 关闭类型检查(避免);unknown 表示未知(需守卫后才可用)。安全处理外部数据用 unknown。

1
2
3
4
5
6
7
8
9
let risky: any = 'text'; // any:不检查,慎用
risky.method(); // 编译通过但运行时可能崩
// unknown:安全
let data: unknown = getApi();
if (typeof data === 'string') {
console.log(data.length); // 守卫后可用
}
// 断言 unknown:data as string
// 优先 unknown,避免 any

类型断言

as 断言告诉编译器你比它懂。断言不改变运行时,只是编译期声明。滥用会掩盖错误。

1
2
3
4
5
6
7
const el = document.getElementById('btn') as HTMLButtonElement;
// 不推荐双重断言(有坑):
// const n = value as unknown as number;
// 更安全:先守卫再断言
const json = JSON.parse(text) as User[];
// as const:字面量收窄
const modes = ['dev', 'prod'] as const;

非空断言

! 后缀断言值非 null/undefined。仅当确定时用,否则运行时可能崩溃。

1
2
3
4
5
6
7
8
9
let name: string | null = getMaybe();
const len = name!.length; // 断言非空(有风险)
// 更安全写法:
if (name) {
const l2 = name.length;
}
// 或空值合并:
const l3 = name?.length ?? 0;
// 非空断言是编译期承诺,运行时仍是 null 会崩

字面量类型

字面量类型把类型精确到具体值:'up'、42、true。配合联合枚举选项。

1
2
3
4
5
6
7
8
let direction: 'up' | 'down' = 'up';
// direction = 'sideways'; // 错误:不在联合里
const yes: true = true;
// 对象属性收窄:
const config = {
mode: 'production',
} as const; // mode: 'production' 字面量
// as const 让对象成员变只读字面量类型

解构与类型

解构赋值保留类型。函数参数解构需标注整个参数对象类型。

1
2
3
4
5
6
7
8
9
10
interface User { name: string; age: number; }
const { name, age }: User = getUser();
// 函数参数解构:
function show({ name, age }: User) {
console.log(name, age);
}
// 数组解构:
const [first, second] = [1, 2] as const;
// 可选属性解构:
const { name = 'guest' }: { name?: string } = data;

3.类型系统

基础类型、对象/数组/元组、接口、泛型、类型别名与类型操作。

基础类型

string、number、boolean、null、undefined、void、symbol、bigint 基础类型集合。

1
2
3
4
5
6
7
8
const s: string = 'text';
const n: number = 42;
const b: boolean = true;
const v: void = undefined; // 无返回值函数
const nl: null = null;
const u: undefined = undefined;
const sym: symbol = Symbol('id');
const big: bigint = 10n;

数组与元组

number[] 数组、[string, number] 元组(定长定序)、readonly 只读数组。

1
2
3
4
5
6
7
8
9
const nums: number[] = [1, 2, 3];
const strs: Array<string> = ['a', 'b']; // 泛型写法
// 元组:
let pair: [string, number] = ['age', 30];
// pair[0] = 42; // 错误:类型不匹配
// 只读数组:
const fixed: readonly number[] = [1, 2];
// fixed.push(3); // 错误:只读
// 可选元组元素:type T = [string, number?]

对象类型

对象类型描述形状:属性、可选 ?、只读 readonly、方法签名。

1
2
3
4
5
6
7
8
9
10
11
12
interface Point {
readonly x: number; // 只读
y: number;
label?: string; // 可选
}
const p: Point = { x: 1, y: 2 };
// p.x = 10; // 错误:readonly
// 方法:
interface Greeter {
greet(name: string): string;
// 或 greet: (name: string) => string;
}

interface 与 type

interface 定义对象形状(可扩展),type 别名更灵活(联合/交叉/元组)。日常优先 interface。

1
2
3
4
5
6
7
8
9
10
11
interface User {
name: string;
}
// interface 可合并/继承:
interface Admin extends User {
permissions: string[];
}
// type 别名:
type ID = string | number; // 联合
type Pair = [string, number]; // 元组
type Shape = { area: number } & { color: string }; // 交叉

枚举

enum 命名常量集合:数字枚举、字符串枚举、const enum。字符串枚举更常用。

1
2
3
4
5
6
7
8
9
10
11
12
13
enum Color {
Red, // 0
Green, // 1
Blue, // 2
}
enum Status {
Active = 'active',
Inactive = 'inactive',
}
const c: Color = Color.Green;
const s: string = Status.Active; // 'active'
// 反向映射:Color[0] === 'Red'(数字枚举)
// 只想要类型:type S = 'active' | 'inactive'

泛型

泛型把类型参数化:T 是类型参数。函数/类/接口通用复用,编译期确定。

1
2
3
4
5
6
7
8
9
10
11
function identity<T>(value: T): T {
return value;
}
const s = identity('hello'); // string
const n = identity(42); // number
// 泛型接口:
interface Box<T> {
value: T;
}
const box: Box<number> = { value: 42 };
// 多参数:function pair<A, B>(a: A, b: B)

keyof 与索引

keyof 取对象键联合、索引访问 T[K]、映射类型。类型操作的核心工具。

1
2
3
4
5
6
7
8
9
10
interface User { name: string; age: number; }
type Keys = keyof User; // 'name' | 'age'
// 索引访问:
type NameType = User['name']; // string
// 映射类型:
type Readonly<T> = {
readonly [K in keyof T]: T[K];
};
// Partial<T>、Required<T>、Pick<T,K> 内置工具
// 部分可选:type PartialUser = Partial<User>

内置工具类型

Partial/Omit/Pick/Record/Exclude/ReturnType 等映射类型简化常见转换。

1
2
3
4
5
6
7
type PartialUser = Partial<User>; // 全部可选
type PickName = Pick<User, 'name'>; // 只取 name
type NoAge = Omit<User, 'age'>; // 去掉 age
type Rec = Record<string, number>; // 键值映射
type WithoutZero = Exclude<0 | 1 | 2, 0>; // 1 | 2
type R = ReturnType<typeof fn>; // 函数返回类型
// Parameters<typeof fn> 参数类型

模板字面量类型

模板字符串语法构造字符串类型。结合联合生成排列组合。字符串解析类型级技巧。

1
2
3
4
5
6
7
8
type Event = `on${'Click' | 'Hover'}`;
// Event = 'onClick' | 'onHover'
type Size = `${'small' | 'large'}-${number}`;
// Size = 'small-1' | 'large-2' ...
// 从字符串提取:
// type Extracted = 'a:b'.split<'a:b', ':'>; // 类型级 split
// 简单字符串解析:
// type First = 'abc' extends `${infer F}bc` ? F : never; // 'a'

4.类型守卫与空值

类型守卫、类型收缩、可选链、null 处理与引用语义。

类型守卫

typeof、instanceof、in 判断收窄联合类型。分支内类型自动收缩。

1
2
3
4
5
6
7
8
9
10
11
function f(v: string | number | Date) {
if (typeof v === 'string') {
v.toUpperCase(); // string
} else if (v instanceof Date) {
v.getTime(); // Date
} else {
v.toFixed(2); // number
}
}
// in 守卫:
if ('permissions' in user) { /* Admin */ }

类型收缩

守卫后类型在作用域内收缩(narrowing)。null 检查、truthy 检查都会收窄。

1
2
3
4
5
6
7
8
9
10
11
let value: string | null = getMaybe();
if (value) {
value.length; // string(收窄)
}
// 真值收缩:
function f(s: string | undefined) {
s ?? console.log('missing'); // 空值合并收窄
}
// 判空后:
value = null;
if (value === null) return; // 之后 value 非 null

可辨识联合

可辨识联合:共享判别字段(kind/type),switch 后类型精确收缩。状态机建模。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
type Shape =
| { kind: 'circle'; radius: number }
| { kind: 'square'; side: number };
function area(s: Shape): number {
switch (s.kind) {
case 'circle': return Math.PI * s.radius ** 2;
case 'square': return s.side * s.side;
// 穷尽检查:
default: {
const _exhaustive: never = s;
return _exhaustive;
}
}
}

可选链与空合并

?. 安全访问、?? 空值兜底、??= 赋默认。链式深访问防 null 崩溃。

1
2
3
4
5
6
7
8
9
const name = user?.profile?.name ?? 'guest';
const count = data?.items?.length ?? 0;
// 空合并赋值:
let settings = getConfig();
settings ??= { theme: 'dark' };
// 可选调用:
callback?.();
// ?? 与 || 区别:
// 0 ?? 'x' 是 0;0 || 'x' 是 'x'

null 与 undefined

strictNullChecks 下 null/undefined 不能赋给普通类型。需显式联合或处理。

1
2
3
4
5
6
7
8
9
let name: string | null = null; // 联合包含 null
let title: string | undefined;
// 函数返回可空:
function find(): User | null {
return Math.random() > 0.5 ? null : { name: 'x' };
}
const u = find();
if (u) { u.name; } // 收缩后访问
// 断言安全:u!.name 或 u ?? { name: '?' }

引用语义

TS 不改变 JS 引用语义:对象按引用共享、数组浅拷贝。类型层面只描述形状。

1
2
3
4
5
6
7
8
9
const a = { x: 1 };
const b = a; // 引用共享
b.x = 99;
console.log(a.x); // 99
// 拷贝仍是浅拷贝:
const c = { ...a };
c.x = 1; // a.x 不变
// TS 类型不保证不可变,除非 readonly
// 深度冻结用 as const + 库

自定义类型守卫

is 语法声明函数是类型守卫:返回 boolean 且参数收窄。过滤数组常用。

1
2
3
4
5
6
7
8
9
function isString(v: unknown): v is string {
return typeof v === 'string';
}
const values: unknown[] = ['a', 1, 'b', null];
const strs = values.filter(isString);
// strs 类型是 string[](守卫生效)
// 箭头函数写法:
const s2 = values.filter(
(v): v is string => typeof v === 'string');

断言函数

asserts 断言函数声明不变量:返回 void 但调用后类型收窄。抛错则中断。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function assertString(v: unknown): asserts v is string {
if (typeof v !== 'string') {
throw new Error('期望 string');
}
}
function process(v: unknown): void {
assertString(v);
v.toUpperCase(); // 收窄为 string
}
// 无条件断言:
function assert(cond: unknown): asserts cond {}
// 断言值:
function assertNonNull<T>(v: T): asserts v is NonNullable<T> {}
// 用于运行时不变量 + 类型收窄双保险

5.流程控制

分支、循环、switch 与类型收缩的配合。

if / else

if/else 分支 + 类型守卫。条件表达式收窄变量类型。

1
2
3
4
5
6
7
8
9
function describe(v: string | number) {
if (typeof v === 'string') {
return `字符串: ${v.toUpperCase()}`;
} else {
return `数字: ${v.toFixed(2)}`;
}
}
// 多分支 else-if + 守卫逐层收窄
// truthy 检查:if (value) 收窄非空

switch 与穷尽

switch 处理可辨识联合。default 分支用 never 检查所有情况已覆盖。

1
2
3
4
5
6
7
8
9
10
11
12
13
type Action =
| { type: 'add'; n: number }
| { type: 'reset' };
function reducer(a: Action) {
switch (a.type) {
case 'add': return a.n;
case 'reset': return 0;
default: {
const exhaustive: never = a; // 缺分支则编译错
return exhaustive;
}
}
}

循环

for/for...of/while 遍历。数组迭代 for...of,需要索引 for 或 entries。

1
2
3
4
5
6
7
8
9
for (let i = 0; i < 10; i++) { }
for (const item of items) { }
for (const [i, v] of items.entries()) {
console.log(i, v);
}
let n = 0;
while (n < 5) { n++; }
// 遍历对象键:
for (const key of Object.keys(obj) as (keyof typeof obj)[]) { }

三元与类型

三元分支两侧类型会联合。条件返回不同类型时结果是联合类型。

1
2
3
4
5
6
const result = cond ? 'yes' : 0;
// result 类型是 'yes' | 0(字面量联合)
// 需要统一类型时显式标注:
const msg: string = cond ? 'yes' : String(0);
// 嵌套三元可读性差,改用守卫
// 空值:v ?? fallback

break 与 continue

continue 跳本轮、break 退出、带标签 break/continue 控制嵌套。

1
2
3
4
5
6
7
8
9
10
11
for (let i = 0; i < 10; i++) {
if (i % 2 === 0) continue;
if (i > 7) break;
}
outer:
for (const a of list) {
for (const b of a.items) {
if (b.done) continue outer;
}
}
// 类型不变,纯控制流

提前返回

守卫子句提前 return 减少嵌套。空值检查后类型收缩。

1
2
3
4
5
6
7
8
function process(u: User | null) {
if (u === null) return; // 提前返回
if (u.age < 18) return;
console.log(u.name); // 已收窄为非空
}
// 多守卫让主逻辑扁平
// 空值合并提前给默认:
const name = u?.name ?? 'guest';

do...while

do...while 先执行一次再判断。至少执行一次的循环用。类型不参与。

1
2
3
4
5
6
7
8
9
10
11
let attempts = 0;
do {
attempts++;
const ok = tryOnce();
if (ok) break;
} while (attempts < 3);
// 至少执行一次,再判断条件
// while:先判断再执行(可能零次)
// 使用场景:
// 重试、菜单选择、输入校验
// 注意死循环风险:条件必须最终为假

对象遍历

Object.keys 遍历对象键。类型断言 keyof 保证安全。值遍历用 Object.values。

1
2
3
4
5
6
7
8
9
10
11
const config = { host: 'x', port: 3000 };
// 键遍历(需断言):
for (const key of Object.keys(config) as (keyof typeof config)[]) {
console.log(key, config[key]);
}
// 值遍历:
for (const value of Object.values(config)) { }
// 键值对:
for (const [k, v] of Object.entries(config)) { }
// 记录类型遍历:
// Object.keys 返回 string[],断言后取值安全

6.函数

函数标注、可选/默认参数、重载、剩余参数与 this。

函数类型

函数类型标注:参数类型与返回类型。箭头函数类型与函数声明等价。

1
2
3
4
5
6
7
8
9
10
11
// 函数声明:
function add(a: number, b: number): number {
return a + b;
}
// 箭头函数:
const add = (a: number, b: number): number => a + b;
// 函数类型变量:
type Fn = (a: number, b: number) => number;
const f: Fn = add;
// void 返回:
function log(msg: string): void { console.log(msg); }

可选与默认参数

? 可选参数、= 默认参数。默认参数隐含可选。可选参数在必选参数之后。

1
2
3
4
5
6
7
8
9
10
11
function greet(name: string, title?: string): string {
return title ? `${title} ${name}` : name;
}
// 默认参数:
function mul(a: number, b = 2): number {
return a * b;
}
mul(3); // 6
// 默认参数可省略不传
// 可选参数放后面:
// greet('Nick', undefined) 也可以

剩余参数

...rest 收集不定数量参数为数组。rest 参数需标注数组类型。

1
2
3
4
5
6
7
8
9
function sum(...nums: number[]): number {
return nums.reduce((a, b) => a + b, 0);
}
sum(1, 2, 3); // 6
// 泛型 rest 保留元组:
function tuple<T extends unknown[]>(...args: T): T {
return args;
}
const t = tuple(1, 'a', true); // [number, string, boolean]

重载签名

重载:多个签名声明 + 一个实现。调用时按签名匹配。按参数组合约束返回类型。

1
2
3
4
5
6
7
8
function pick(obj: Record<string, unknown>, key: string): unknown;
function pick(obj: number[], index: number): number;
function pick(obj: any, key: string | number): unknown {
return obj[key];
}
// 实现签名不对外可见
// 重载按顺序匹配,把宽泛的放最后
// 返回类型差异场景:DOM API 类型常用

泛型函数

泛型参数约束:T extends 约束。约束限定类型后可用约束上的成员。

1
2
3
4
5
6
7
8
9
10
function first<T extends string | number[]>(arr: T): T[number] {
return arr[0];
}
const s = first('hello'); // string
const n = first([1, 2, 3]); // number
// 约束调用:
function getLen<T extends { length: number }>(v: T): number {
return v.length;
}
// 多泛型:<K, V extends keyof K>

this 类型

this 参数标注 this 类型。方法链返回 this 实现链式调用。箭头函数不绑定 this。

1
2
3
4
5
6
7
8
9
10
11
12
13
class Builder {
private items: string[] = [];
add(item: string): this {
this.items.push(item);
return this; // 链式
}
}
const b = new Builder().add('a').add('b');
// 显式 this 参数(放首位):
function log(this: { name: string }) {
console.log(this.name);
}
// 箭头函数继承外层 this

回调与函数参数

回调函数作为参数:用函数类型标注。数组高阶函数 map/filter/reduce 的类型推导。

1
2
3
4
5
6
7
8
9
function withLog(fn: (n: number) => number) {
return fn(42);
}
withLog(n => n * 2); // 参数类型自动推导
// 数组高阶函数:
const doubled = [1, 2, 3].map(n => n * 2);
const evens = [1, 2, 3, 4].filter(n => n % 2 === 0);
const total = [1, 2, 3].reduce((acc, n) => acc + n, 0);
// 回调 this 类型注意,避免丢失

函数约束技巧

参数联合收窄、可选回调、返回推断。函数参数尽量用接口而非具体类。

1
2
3
4
5
6
7
8
9
10
function handle(v: string | number, cb?: (r: string) => void) {
const r = typeof v === 'string' ? v.toUpperCase() : String(v);
cb?.(r); // 可选回调
}
// 参数用接口(结构类型):
interface HasId { id: number }
function findById<T extends HasId>(arr: T[], id: number): T | undefined {
return arr.find(x => x.id === id);
}
// 结构类型:形状匹配即可,不必是同类实例

7.字符串

模板字符串、常用方法、正则与字符处理。

模板字符串

反引号模板字符串:${} 插值、多行保留。类型仍是 string。

1
2
3
4
5
6
7
8
9
10
11
12
const name = 'Nick';
const greeting = `Hello, ${name}!`;
// 多行:
const lines = `
line 1
line 2
`;
// 表达式:
const total = `Sum: ${1 + 2}`;
// 模板字面量类型:
// type T = `id-${string}`
// 类型仍是 string,模板只是语法糖

常用方法

slice/substring 截取、toUpperCase 转换、split 分割、includes/startsWith 查询、replace 替换。

1
2
3
4
5
6
7
8
9
const s = 'TypeScript';
const sub = s.slice(0, 4); // 'Type'
const upper = s.toUpperCase(); // 'TYPESCRIPT'
const parts = s.split(''); // 字符数组
s.includes('Script'); // true
s.startsWith('Type'); // true
s.endsWith('t'); // true
const r = 'a-b-c'.replace(/-/g, '_'); // 'a_b_c'
const padded = '7'.padStart(3, '0'); // '007'

模板字面量类型

类型级模板字符串:${} 拼接类型。推断 infer 提取字符串结构。

1
2
3
4
5
6
7
8
type Route = `/users/${string}/profile`;
const good: Route = '/users/123/profile';
// 类型级提取:
type ExtractId<S extends string> =
S extends `/users/${infer Id}/profile` ? Id : never;
type Id = ExtractId<'/users/42/profile'>; // '42'
// 大写转换:Uppercase<T>、Lowercase<T>
// 组合:type All = `${'a'|'b'}${'1'|'2'}` // 'a1'|'a2'|'b1'|'b2'

字符与码点

length 是 UTF-16 码元数(emoji 算两个)。码点遍历用 for...of / Array.from。

1
2
3
4
5
6
7
8
9
const emoji = '👋';
console.log(emoji.length); // 2(代理对)
console.log([...emoji].length); // 1
const s = 'abc';
for (const ch of s) { } // 逐码点
// 码点访问:
const first = Array.from(s)[0];
// charCodeAt/fromCodePoint 处理码点:
String.fromCodePoint(128075); // '👋'

正则

RegExp 匹配替换。match 返回捕获组数组,matchAll 全局遍历。类型层面 RegExp 固定。

1
2
3
4
5
6
7
8
9
10
11
const re = /(\w+)@(\w+)/g;
const all = s.matchAll(re);
for (const m of all) {
console.log(m[1], m[2]); // 捕获组
}
// 替换带捕获:
const r = '2026-01-01'.replace(/(\d{4})-(\d{2})-(\d{2})/, '$3/$2/$1');
// 断言非空:
const match = s.match(/(\w+)@/);
if (match) { console.log(match[1]); }

区域与比较

localeCompare 区域比较、toLocaleLowerCase 区域转换、Intl 格式化数字日期。

1
2
3
4
5
6
const arr = ['ä', 'a', 'z'].sort((a, b) => a.localeCompare(b, 'zh'));
const num = (1234.5).toLocaleString('zh-CN'); // '1,234.5'
const date = new Date().toLocaleDateString('zh-CN');
// Intl.NumberFormat:
const nf = new Intl.NumberFormat('zh-CN', { style: 'currency', currency: 'CNY' });
// 区域列表:Intl.supportedValuesOf('language')

转义字符

字符串转义:\n 换行、\t 制表、\\ 反斜杠、\u 码点。单双引号内转义。

1
2
3
4
5
6
7
8
9
10
const nl = '第一行\n第二行';
const tab = 'a\tb'; // a 空格制表 b
const backslash = 'C:\\path'; // C:\path
const quote = 'He said \'hi\'';
const uni = '\u4e2d'; // '中'
const code = '\u{1F600}'; // emoji 码点
// 模板字符串不需要转义单双引号
const tmpl = `She said "hi" and 'bye'`;
// 常用:\n 换行 \t 缩进 \\ 路径
// JSON 输出要转义引号

反转与比较

字符串反转用 split/数组、去空格、去重。比较用 localeCompare 或规范比较。

1
2
3
4
5
6
7
8
9
10
11
12
const s = 'hello';
// 反转:
const rev = [...s].reverse().join(''); // 'olleh'
// 去首尾空白:
const t = ' text '.trim();
// 去所有空白:
const compact = s.replace(/\s+/g, '');
// 重复:'ab'.repeat(3) // 'ababab'
// 判断回文:
const isPalindrome = s === [...s].reverse().join('');
// 规范比较(忽略大小写):
s.toLowerCase() === 'HELLO'.toLowerCase()

8.集合与对象

数组、对象、Map/Set 与不可变更新模式。

数组操作

push/pop 尾部、unshift/shift 头部、splice 增删、slice 拷贝、includes/indexOf 查询。

1
2
3
4
5
6
7
8
9
const arr = [1, 2, 3];
arr.push(4); // [1,2,3,4]
const last = arr.pop(); // 4
arr.unshift(0); // [0,1,2,3]
const first = arr.shift(); // 0
const removed = arr.splice(1, 1); // 删除
const copy = arr.slice(); // 浅拷贝
arr.includes(2);
arr.indexOf(2); // 首次位置或 -1

map / filter / reduce

函数式遍历:map 转换、filter 过滤、reduce 聚合、find 查找、every/some 判断。返回新数组不改原数组。

1
2
3
4
5
6
7
8
9
const nums = [1, 2, 3, 4];
const doubled = nums.map(n => n * 2);
const evens = nums.filter(n => n % 2 === 0);
const sum = nums.reduce((acc, n) => acc + n, 0);
const found = nums.find(n => n > 2); // 3 | undefined
const ok = nums.every(n => n > 0); // true
const has = nums.some(n => n === 4); // true
// 链式:
nums.filter(n => n % 2).map(n => n * 10).reduce((a, b) => a + b, 0);

对象操作

展开合并 {...a, ...b}、Object.keys/values/entries 遍历、keyof 类型键。

1
2
3
4
5
6
7
8
9
10
const base = { id: 1, name: 'Nick' };
const extended = { ...base, age: 30 }; // 合并
const override = { ...base, name: 'New' }; // 覆盖
const keys = Object.keys(base);
// 类型安全遍历:
for (const key of Object.keys(base) as (keyof typeof base)[]) {
console.log(base[key]);
}
// entries:
for (const [k, v] of Object.entries(base)) { }

Map

Map 任意键映射:set/get/has/delete/size。保持插入顺序,O(1) 查找。

1
2
3
4
5
6
7
8
9
10
11
const scores = new Map<string, number>();
scores.set('alice', 90);
const v = scores.get('alice'); // number | undefined
scores.has('bob'); // false
scores.delete('alice');
scores.size;
// 遍历:
for (const [k, val] of scores) { }
for (const key of scores.keys()) { }
// 初始化:
new Map([['a', 1], ['b', 2]]);

Set

Set 去重集合:add/delete/has/size。去重数组、差集并集。

1
2
3
4
5
6
7
8
9
10
const set = new Set<number>();
set.add(1).add(2).add(1); // {1, 2}
set.has(1); // true
set.delete(2);
// 数组去重:
const unique = [...new Set([1, 2, 2, 3])]; // [1, 2, 3]
// 并集:
const union = new Set([...a, ...b]);
// 交集:
const inter = new Set([...a].filter(x => bSet.has(x)));

不可变更新

用展开/拷贝更新而非原地改。对象替换、数组增删返回新引用。React 状态常见。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
interface State {
items: string[];
count: number;
}
const next: State = {
...state,
items: [...state.items, 'new'], // 追加
count: state.count + 1,
};
// 删除一项:
const filtered = state.items.filter(x => x !== 'old');
// 更新某项:
const updated = state.items.map((x, i) => i === 0 ? 'new' : x);
// readonly 帮助类型检查防原地改

元组与 Record

元组定长、Record 键值映射。对象字面量 as const 转常量。

1
2
3
4
5
6
7
8
9
10
11
const point: [number, number] = [10, 20];
const [x, y] = point; // 解构
// Record:
type Config = Record<string, boolean>;
const flags: Config = { debug: true };
// as const 常量对象:
const statusMap = {
active: '运行中',
stopped: '已停止',
} as const;
// statusMap.active 类型是 '运行中' 字面量

WeakMap / WeakSet

WeakMap/WeakSet 键必须对象且弱引用。不阻止 GC。缓存元数据/副作用用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const meta = new WeakMap<object, { visited: boolean }>();
const node = document.getElementById('a');
if (node) meta.set(node, { visited: true });
// 键只能是对象:
// meta.set(1, {}) // 错误
// 不可遍历(无 keys/size)
// 用途:
// 1. 给对象挂私有元数据
// 2. 缓存计算结果避免泄漏
// 3. 监听器标记
// WeakSet 用于标记集合:
const processed = new WeakSet<object>();
processed.add(obj);
processed.has(obj); // true

9.性能与内存

垃圾回收下的内存观、大型数据、字符串优化与监控。

垃圾回收

TS/JS 有 GC,无需手动释放。对象不再引用即回收。长生命周期对象小心闭包持有。

1
2
3
4
5
6
7
8
9
10
// 对象离开作用域且无引用后由 GC 回收
function create() {
const big = new Array(1e6);
return () => big.length; // 闭包持有 big
}
// 闭包让 big 存活:
const f = create(); // big 无法回收
// 释放:
f = null; // 解除引用
// 避免全局缓存无限增长

大型数组

大数组操作考虑 TypedArray/流式。filter/map 产生新数组注意开销。循环复用更省。

1
2
3
4
5
6
7
8
9
10
// TypedArray 处理二进制:
const buf = new Float64Array(1e6);
// 大数组避免反复 map/filter 链:
// BAD:多趟遍历
// GOOD:一次循环处理
const src = new Array(1e6).fill(0);
let sum = 0;
for (let i = 0; i < src.length; i++) sum += src[i];
// 二进制:DataView + ArrayBuffer
// 数值精度:BigInt 大整数,BigInt.asIntN 截断

字符串内存

字符串不可变,拼接产生新字符串。大量拼接用数组 join 或模板。字符串驻留(interning)。

1
2
3
4
5
6
7
8
9
// 循环拼接慢:
let s = '';
for (let i = 0; i < 1e5; i++) s += i; // BAD
// 数组 join:
const parts: string[] = [];
for (let i = 0; i < 1e5; i++) parts.push(String(i));
const s2 = parts.join(''); // GOOD
// 或模板字符串分段
// 长字符串切片用 slice(O(n))

WeakRef 与缓存

WeakRef 弱引用不阻止回收、WeakMap/WeakSet 键弱引用。缓存或记录副作用用弱容器。

1
2
3
4
5
6
7
8
9
const cache = new WeakMap<object, number>();
const obj = { id: 1 };
cache.set(obj, compute(obj));
// obj 被回收后条目自动消失
// WeakRef:
const ref = new WeakRef(obj);
const alive = ref.deref(); // object | undefined
// FinalizationRegistry 监听回收:
const reg = new FinalizationRegistry(held => console.log('collected', held));

性能技巧

避免 any 拖慢优化、减少重分配、缓存结果。V8 优化依赖稳定形状对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
// 避免动态属性改变对象形状:
// BAD:
const obj: Record<string, number> = {};
obj.a = 1; obj.b = 2; // 形状变化
// GOOD:声明完整形状
const obj = { a: 0, b: 0 };
// 缓存长链:
const len = arr.length; // 循环中避免重复取
// 避免隐式类型转换:
const s = String(n) + x;
// 热路径避免闭包分配:
for (let i = 0; i < n; i++) { }
// 而非 map 每项新建箭头函数

内存监控

Node 查看内存:process.memoryUsage()、--max-old-space-size。浏览器用 Performance API。

1
2
3
4
5
6
7
8
9
// Node:
console.log(process.memoryUsage());
// heapUsed 已用堆、heapTotal 总堆
// 增大堆:node --max-old-space-size=4096 app.js
// 浏览器:
performance.measureMemory?.()
.then(m => console.log(m.bytes));
// 堆快照:Chrome DevTools Memory 面板
// 泄漏特征:heapUsed 持续上升不回落

闭包与内存

闭包持有外部变量,延长其生命周期。循环中闭包陷阱。释放引用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function makeCounter() {
let count = 0; // 被闭包捕获
return () => ++count; // 存活
}
const c = makeCounter();
c(); // 1
// 循环中闭包捕获(var 陷阱):
// BAD:var 共享
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i)); // 3 3 3
}
// GOOD:let 块级捕获
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i)); // 0 1 2
}
// 释放长生命周期闭包:
// fn = null

TypedArray 与二进制

TypedArray 处理二进制数值:Uint8Array/Float64Array。视图共享底层 buffer。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const bytes = new Uint8Array(16); // 16 字节
bytes[0] = 255;
// 创建自已有数据:
const arr = new Uint8Array([1, 2, 3]);
// 浮点:
const floats = new Float64Array(8);
// 底层共享:
const buffer = new ArrayBuffer(16);
const view = new DataView(buffer);
view.setInt32(0, 42);
view.getInt32(0); // 42
// 转普通数组:
const plain = Array.from(bytes);
// 编码:
new TextEncoder().encode('中文');
// 大文件/网络协议/Canvas 像素常用

10.类与面向对象

class、访问修饰符、继承、抽象类、泛型类。

class 基础

class 语法:字段、构造器、方法。字段可标注类型和可见性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Person {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
greet(): string {
return `Hi, I'm ${this.name}`;
}
}
const p = new Person('Nick', 30);
// 参数属性简写:
class P2 {
constructor(public name: string, private age: number) {}
}

访问修饰符

public 公开、private 私有、protected 受保护、readonly 只读。都是编译期检查。

1
2
3
4
5
6
7
8
9
10
11
12
class Account {
public owner: string; // 默认 public
private balance = 0; // 私有
protected type = 'basic'; // 子类可访问
readonly id: string; // 只读
constructor(owner: string) {
this.owner = owner;
this.id = crypto.randomUUID();
}
}
// # 私有字段(运行时私有,ES2022):
class C { #secret = 1; get() { return this.#secret; } }

继承与 override

extends 继承、super() 调父构造、override 覆盖父方法。子类是父类(is-a)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Animal {
constructor(public name: string) {}
speak(): string { return `${this.name} makes a sound`; }
}
class Dog extends Animal {
constructor(name: string, public breed: string) {
super(name); // 先调父构造
}
override speak(): string { // override 显式标记
return `${this.name} barks`;
}
}
const d = new Dog('Rex', 'Husky');
// d 是 Dog 也是 Animal

抽象类与接口

abstract 类不能实例化,抽象方法子类必须实现。接口约束形状。抽象类可有实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
abstract class Shape {
abstract area(): number; // 子类必须实现
describe(): string {
return `Area: ${this.area()}`;
}
}
class Circle extends Shape {
constructor(private r: number) { super(); }
override area(): number { return Math.PI * this.r ** 2; }
}
// 接口约束:
interface HasArea { area(): number }
function printArea(s: HasArea) { console.log(s.area()); }

implements

class implements 接口:类必须满足接口形状。一个类可实现多个接口。

1
2
3
4
5
6
7
8
9
10
11
12
interface Runnable {
run(): void;
}
interface Jumpable {
jump(): void;
}
class Player implements Runnable, Jumpable {
run(): void { console.log('running'); }
jump(): void { console.log('jump'); }
}
// 实现缺方法则编译错误
// implements 只约束形状,不要求继承关系

泛型类

泛型类:类型参数用于字段与方法。约束限定泛型范围。

1
2
3
4
5
6
7
8
9
10
11
class Box<T> {
private value: T;
constructor(value: T) { this.value = value; }
get(): T { return this.value; }
set(v: T): void { this.value = v; }
}
const numBox = new Box<number>(42);
const strBox = new Box('hi'); // 推断 string
// 静态成员不能引用类型参数:
// static arr: T[] // 错误
// 泛型约束:class Box<T extends { id: number }>

getter / setter

get/set 访问器包装字段读写。可加校验与计算逻辑。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Temperature {
private _celsius = 0;
get celsius(): number { return this._celsius; }
set celsius(value: number) {
if (value < -273.15) throw new Error('绝对零度以下');
this._celsius = value;
}
get fahrenheit(): number {
return this._celsius * 9 / 5 + 32;
}
}
const t = new Temperature();
t.celsius = 25;
console.log(t.fahrenheit); // 77

静态成员

static 定义类级字段方法。静态成员不依赖实例。工厂方法与常量用 static。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class MathHelper {
static readonly PI = 3.14159; // 类常量
static max(arr: number[]): number {
return Math.max(...arr);
}
// 工厂方法:
static create(name: string): MathHelper {
return new MathHelper(name);
}
constructor(private name: string) {}
}
MathHelper.PI;
MathHelper.max([1, 5, 3]);
// 静态成员通过类名访问:
// new MathHelper().PI // 错误
// 静态属性初始化在类定义时执行

11.异常处理

throw/try-catch、错误类型、自定义错误、异步错误。

throw / try-catch

throw 抛错、try-catch 捕获、finally 收尾。catch 变量默认 unknown 需收窄。

1
2
3
4
5
6
7
8
9
10
11
try {
const n = JSON.parse(text);
if (typeof n !== 'number') throw new Error('需要数字');
} catch (err) {
if (err instanceof Error) {
console.log(err.message); // 收窄后访问
}
} finally {
cleanup(); // 无论成败都执行
}
// 不捕获则向上抛,未处理则崩溃

自定义错误

extends Error 自定义错误类。携带额外信息。错误名用于区分类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class ValidationError extends Error {
constructor(public field: string, message: string) {
super(message);
this.name = 'ValidationError';
}
}
try {
throw new ValidationError('email', '邮箱格式错误');
} catch (err) {
if (err instanceof ValidationError) {
console.log(err.field, err.message);
}
}
// 设置原型链确保 instanceof:
// Object.setPrototypeOf(this, ValidationError.prototype)

常见错误类型

Error 基类:TypeError 类型错误、RangeError 越界、ReferenceError 引用错误。判断用 instanceof。

1
2
3
4
5
6
7
8
9
10
11
12
try {
// TypeError: 调用不存在的方法
// RangeError: 数组越界 / 递归过深
// ReferenceError: 引用未声明变量
const arr = [1, 2];
arr[5].toFixed(); // TypeError
} catch (err) {
if (err instanceof TypeError) { }
else if (err instanceof RangeError) { }
// instanceof Error 兜底
}
// 用错误 name 属性判断跨 realm 情况

异步错误

async 函数 throw 变 rejected Promise。await 时 try-catch 捕获。Promise.catch 链。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
async function load(): Promise<void> {
try {
const res = await fetch('/api');
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const data = await res.json();
} catch (err) {
console.error('加载失败', err);
}
}
// Promise 链:
fetch('/api').then(r => r.json()).catch(e => {
console.error(e);
});
// 未捕获的 rejected Promise 触发 unhandledrejection

错误边界

模块边界捕获并转换错误类型。第三方错误统一包装。错误不逃逸导致崩溃。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function safeParse<T>(json: string): T {
try {
return JSON.parse(json) as T;
} catch {
throw new Error('JSON 解析失败'); // 统一包装
}
}
// 入口捕获:
process.on('uncaughtException', (err) => {
console.error('未捕获异常', err);
process.exit(1);
});
// Node 里同步异常在顶层兜底
// 浏览器:window.onerror / unhandledrejection

错误处理模式

可预测错误返回结果,意外错误抛异常。Result 风格(ok/err)与 throw 各有适用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 可预期失败:返回 null / 结果对象
type Result<T> = { ok: true; value: T } | { ok: false; error: string };
function parseNum(s: string): Result<number> {
const n = Number(s);
return Number.isNaN(n)
? { ok: false, error: '不是数字' }
: { ok: true, value: n };
}
const r = parseNum('abc');
if (r.ok) console.log(r.value);
else console.log(r.error);
// 意外错误:throw + 上层捕获
// 规则:调用方能处理的用返回,否则抛
// 避免吞异常:catch 后至少 log

错误信息质量

错误信息包含上下文:什么、哪里、如何修。自定义错误携带字段。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class HttpError extends Error {
constructor(
public status: number,
public url: string,
message: string,
) {
super(message);
this.name = 'HttpError';
}
}
// GOOD:带上下文
throw new HttpError(404, url, `资源不存在: ${url}`);
// BAD:无信息
// throw new Error('失败了');
// 日志包含堆栈:
console.error(err); // 保留 stack
// 错误原因链:
new Error('外层失败', { cause: innerErr })

未处理拒绝

未捕获的 rejected Promise 触发 unhandledrejection。顶层兜底防静默失败。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Node:
process.on('unhandledRejection', (reason) => {
console.error('未处理的 Promise 拒绝', reason);
});
// 浏览器:
window.addEventListener('unhandledrejection', (e) => {
e.preventDefault();
console.error('未处理拒绝', e.reason);
});
// 同步异常:
process.on('uncaughtException', (err) => {
console.error('未捕获异常', err);
process.exit(1);
});
// 审计:拒绝监听在测试中抓遗漏
// 兜底不是吞错,要记录并定位

12.输入输出

console 输出、Node 文件/流、fetch 网络、类型化 JSON。

console 输出

console.log/info/warn/error、模板输出、%o 格式化、分组与计数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
console.log('Hello');
console.info('info 消息');
console.warn('警告');
console.error('错误');
// 格式化:
console.log('%o', { a: 1 }); // 对象展开
console.table([{ a: 1 }, { a: 2 }]);
// 分组:
console.group('组');
console.log('内容');
console.groupEnd();
// 计时:
console.time('t');
console.timeEnd('t');

fetch 网络

fetch 异步请求。await 响应、json 解析、错误处理、类型断言结果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
async function getUsers(): Promise<User[]> {
const res = await fetch('/api/users');
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return res.json() as Promise<User[]>; // 断言
}
// POST:
await fetch('/api', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: 'Nick' }),
});
// AbortController 超时:
const ac = new AbortController();
setTimeout(() => ac.abort(), 3000);
await fetch(url, { signal: ac.signal });

Node 文件

fs/promises 异步读写。readFile/writeFile/mkdir/readdir 返回 Promise。

1
2
3
4
5
6
7
8
9
10
import { readFile, writeFile, mkdir, readdir } from 'node:fs/promises';
const text = await readFile('data.txt', 'utf8');
await writeFile('out.txt', text.toUpperCase());
await mkdir('dir', { recursive: true });
const files = await readdir('.');
// 流式大文件:
import { createReadStream } from 'node:fs';
const stream = createReadStream('big.log');
// 需要 @types/node:
// npm i -D @types/node

JSON 与类型

JSON.parse/stringify 序列化。parse 结果需守卫验证,stringify 忽略函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
interface User { name: string; age: number }
const u: User = { name: 'Nick', age: 30 };
const json = JSON.stringify(u);
// 解析加校验:
function isUser(v: unknown): v is User {
return typeof v === 'object' && v !== null
&& typeof (v as any).name === 'string'
&& typeof (v as any).age === 'number';
}
const data = JSON.parse(json);
if (isUser(data)) console.log(data.name);
// JSON.parse 返回 any,断言前最好守卫
// 序列化选项:JSON.stringify(u, null, 2) 美化

流式处理

ReadableStream/Web Streams 处理大响应。读取进度、逐块处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
const res = await fetch('/big');
const reader = res.body!.getReader();
const decoder = new TextDecoder();
let total = 0;
while (true) {
const { done, value } = await reader.read();
if (done) break;
total += value.length;
const chunk = decoder.decode(value, { stream: true });
process(chunk);
}
// 大文件流读:
// for await (const chunk of createReadStream('big.log')) { }

环境变量与参数

Node 读取 process.argv / process.env。参数解析、环境变量类型收窄。

1
2
3
4
5
6
7
8
9
10
11
// process.argv[0]=node, [1]=脚本, [2..] 参数
const args = process.argv.slice(2);
// 环境变量:
const port = Number(process.env.PORT ?? 3000);
const mode = process.env.NODE_ENV ?? 'development';
// 判断:
if (mode === 'production') { }
// .env 加载:
// npm i dotenv
// import 'dotenv/config'
// 类型安全环境:process.env 是 Record<string, string | undefined>

浏览器 Web API

localStorage/sessionStorage、navigator、WebSocket 的类型。存储值需序列化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 本地存储:
localStorage.setItem('token', 'abc');
const t = localStorage.getItem('token'); // string | null
// 存对象需序列化:
localStorage.setItem('user', JSON.stringify(user));
// 读回需解析 + 校验:
const raw = localStorage.getItem('user');
if (raw) { const u = JSON.parse(raw) as User; }
// 导航:
if ('geolocation' in navigator) {
navigator.geolocation.getCurrentPosition((pos) => {
console.log(pos.coords.latitude);
});
}
// WebSocket 事件类型:
ws.addEventListener('message', (e: MessageEvent) => {
console.log(e.data);
});

文件读取

input[type=file] 获取 File、FileReader 读文本/DataURL、对象 URL 预览。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const input = document.querySelector('input[type=file]') as HTMLInputElement;
input.addEventListener('change', async () => {
const file = input.files?.[0];
if (!file) return;
// 读文本:
const text = await file.text();
// 读为 DataURL:
const reader = new FileReader();
reader.onload = () => console.log(reader.result);
reader.readAsDataURL(file);
// 预览图片:
const url = URL.createObjectURL(file);
img.src = url;
// File 有 name/size/type
console.log(file.name, file.size, file.type);
});

13.常见误区

日常开发最容易踩的坑与正确写法。

滥用 any

any 关掉类型检查,掩盖真实错误。能用 unknown + 守卫就用。

1
2
3
4
5
6
7
8
9
10
// BAD:any 吞掉错误
function getLen(v: any): number {
return v.length; // 编译通过,运行时可能崩
}
// GOOD:unknown + 守卫
function getLen(v: unknown): number {
if (typeof v !== 'string') return 0;
return v.length;
}
// any 的隐式传播:返回值 any 传染调用方

空值判断

!! 判断真值、== null 同时判 null/undefined、数组空判断。别把 0/'' 当空。

1
2
3
4
5
6
7
8
9
10
// BAD:值可能为 0/''
if (value) { } // 0 是 falsy
// GOOD:显式判 null
if (value !== null && value !== undefined) { }
// 简写 == null 同时判两者:
if (value == null) { } // null 或 undefined
// 数组空判断:
if (arr.length === 0) { }
// 对象空判断:
if (Object.keys(obj).length === 0) { }

async 误用

async 函数必返回 Promise。忘 await 变 Promise 泄漏。forEach 不支持 async 等待。

1
2
3
4
5
6
7
8
9
10
11
12
13
// BAD:forEach 不等待
async function main() {
[1, 2, 3].forEach(async n => { await work(n); });
console.log('done'); // 先输出!
}
// GOOD:for...of
for (const n of [1, 2, 3]) {
await work(n);
}
// BAD:忘 await
const res = fetch('/api'); // 是 Promise
// 并行:
await Promise.all([a(), b()]);

等值比较

=== 严格相等。NaN !== NaN。对象比较引用。深比较需手写或库。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// BAD:== 宽松比较会做类型转换
'1' == 1; // true,坑
// GOOD:用 === 严格比较
'1' === 1; // false
// NaN 特殊:
NaN === NaN; // false
Number.isNaN(NaN); // true
// 对象按引用比较:
{} === {}; // false
// 数组内容比较:
[1, 2].join() === [1, 2].join(); // 简单方案
// 深比较库:
// import { isEqual } from 'lodash-es';
// 性能:深度大对象避免频繁深比较

null 与 undefined 混淆

null 是有意空值,undefined 是未赋值。strictNullChecks 下分别处理。可选链 vs 断言。

1
2
3
4
5
6
7
8
9
10
11
12
// BAD:断言掩盖可能为 null
const len = name!.length; // name 可能真的为 null
// GOOD:先判断
if (name) {
const len = name.length;
}
// 空值合并:
const n = name ?? 'default';
// 可选链安全访问:
user?.profile?.email;
// 用 ?? 不用 ||:0 和 '' 是有效值
const port = port ?? 3000; // port=0 时保留 0

this 丢失

回调中 this 变 undefined。箭头函数绑定或显式绑定。类字段箭头函数常用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Counter {
count = 0;
increment = () => { this.count++; }; // 箭头绑定
}
// BAD:回调中 this 丢失
class C {
value = 1;
method() { return this.value; }
}
const fn = new C().method;
fn(); // undefined 报错
// GOOD:
const fn2 = new C().method.bind(new C());
// 或调用时 .method() 方式

收窄失效

属性访问后收窄丢失。解构后才收窄。函数参数重新赋值会变宽。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// BAD:属性收窄不保持
function f(p: { a?: string }) {
if (p.a) {
p.a.toUpperCase(); // 可能已变
}
}
// GOOD:先存变量
function f2(p: { a?: string }) {
const a = p.a;
if (a) {
a.toUpperCase();
}
}
// 可选属性对象访问两次有竞态
// 参数重新赋值丢失收窄:
// let x = v; 再判断

穷尽检查

可辨识联合 default 用 never 检查。新增类型分支漏处理编译报错。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// GOOD:default 用 never 做穷尽检查
type Event =
| { kind: 'open' }
| { kind: 'close' };
function handle(e: Event) {
switch (e.kind) {
case 'open': break;
case 'close': break;
default: {
const _exhaustive: never = e;
// 新增 kind 时此处编译报错
return _exhaustive;
}
}
}
// BAD:不加 default,新增类型静默漏处理
// 更新类型后编译器提醒所有 switch

拷贝与修改

数组/对象引用共享导致误改。更新要拷贝。sort 原地改数组。

1
2
3
4
5
6
7
8
9
10
11
12
13
const a = [1, 2, 3];
// BAD:sort 原地改
const sorted = a.sort(); // a 也被改
// GOOD:先拷贝
const sorted = [...a].sort();
// BAD:引用共享误改
const b = a;
b.push(4); // a 也变
// 浅拷贝:
const copy = [...a];
// 对象展开浅拷贝:
const o2 = { ...o1 };
// 嵌套深拷贝需逐层处理

14.并发与异步

Promise、async/await、Worker 线程与事件循环。

Promise

Promise 表示异步结果。then 链、catch 错误、finally 收尾。类型标注 Promise<T>。

1
2
3
4
5
6
7
8
9
10
const p: Promise<number> = new Promise((resolve, reject) => {
setTimeout(() => resolve(42), 1000);
});
p.then(n => console.log(n))
.catch(err => console.error(err))
.finally(() => console.log('done'));
// 立即创建:
Promise.resolve(1);
Promise.reject(new Error('x'));
// 泛型错误:Promise<T> 成功值类型 T

async / await

async 函数返回 Promise。await 解包。错误用 try-catch。顶层 await 需 ESM。

1
2
3
4
5
6
7
8
9
10
11
12
async function load(): Promise<User> {
const res = await fetch('/api/user');
if (!res.ok) throw new Error('加载失败');
return res.json() as Promise<User>;
}
// 调用:
const user = await load();
// 错误:
try { await load(); } catch (err) { }
// 并行执行:
const [a, b] = await Promise.all([load(), load()]);
// 顶层 await(ESM .mjs)

并行与竞态

Promise.all 全部完成、allSettled 全部(含失败)、race 最先完成、any 首个成功。

1
2
3
4
5
6
7
8
9
10
11
const tasks = [fetch1(), fetch2(), fetch3()];
// 全部成功(任一失败整体失败):
const all = await Promise.all(tasks);
// 不中断收集所有结果:
const settled = await Promise.allSettled(tasks);
// 首个完成(含失败):
const first = await Promise.race(tasks);
// 首个成功(全败才报 AggregateError):
const any = await Promise.any(tasks);
// 并发限制:
// for 循环分块或 p-limit 库

Worker 线程

Web Worker / Node worker_threads 并行计算。postMessage 通信、transferable 转移所有权。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 主线程:
const worker = new Worker('/worker.js');
worker.postMessage({ n: 42 });
worker.onmessage = (e) => console.log(e.data);
worker.onerror = (e) => console.error(e);
// worker.js:
self.onmessage = (e) => {
const result = heavyCompute(e.data.n);
self.postMessage(result);
};
// Node:
// import { Worker } from 'node:worker_threads';
// 大对象用转移:
// postMessage(buf, [buf.buffer])

事件循环

同步代码先执行,微任务(Promise)先于宏任务(setTimeout)。阻塞会卡住一切。

1
2
3
4
5
6
7
8
9
console.log('1'); // 同步
Promise.resolve().then(() =>
console.log('2')); // 微任务
setTimeout(() => console.log('3'), 0); // 宏任务
console.log('4');
// 输出顺序:1 4 2 3
// 长任务阻塞事件循环:
// for (let i=0;i<1e9;i++){} // 卡住
// 拆块或让出:await new Promise(r => setTimeout(r, 0))

生成器

function* 生成器惰性产出。yield 暂停恢复。Generator 类型标注。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function* count(max: number): Generator<number> {
let i = 0;
while (i < max) {
yield i++; // 暂停产出
}
}
for (const n of count(3)) console.log(n); // 0 1 2
// 手动驱动:
const g = count(2);
g.next(); // { value: 0, done: false }
g.next();
// 无限序列 + 惰性:
function* naturals(): Generator<number> {
let n = 0;
while (true) yield n++;
}
// 与迭代器协议同构

异步迭代器

for await 遍历异步数据源。异步生成器 async function*。流式数据消费。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
async function* generate(): AsyncGenerator<number> {
let i = 0;
while (i < 3) {
await new Promise(r => setTimeout(r, 100));
yield i++;
}
}
// 消费:
for await (const n of generate()) {
console.log(n); // 0 1 2
}
// 异步迭代对象:
const res = await fetch('/big');
const reader = res.body!.getReader();
// Node 流:
// for await (const chunk of createReadStream('f'))
// 惰性 + 背压,内存友好

并发限制

控制同时运行的异步任务数。分批、信号量或 p-limit 思路。避免打爆资源。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
async function mapLimit<T, R>(
items: T[], limit: number, fn: (x: T) => Promise<R>,
): Promise<R[]> {
const results: R[] = [];
let i = 0;
const workers = Array.from({ length: Math.min(limit, items.length) },
async () => {
while (i < items.length) {
const idx = i++;
results[idx] = await fn(items[idx]);
}
});
await Promise.all(workers);
return results;
}
// 用:分批下载/请求,limit 控制并发
// 批量下载等场景避免一次性全发

15.网络与模块

模块系统、import/export、fetch 与类型化 API 封装。

ES 模块

import/export 静态导入导出。TS 类型导出用 export type。模块隔离作用域。

1
2
3
4
5
6
7
8
9
10
11
// utils.ts:
export const version = '1.0';
export function add(a: number, b: number): number { return a + b; }
export type ID = string; // 类型导出
export default class App { } // 默认导出
// main.ts:
import App, { add, version, type ID } from './utils';
// 重命名:
import { add as plus } from './utils';
// 整体导入:
import * as utils from './utils';

类型导入

import type 只导入类型,编译时擦除。避免运行时依赖与循环引用。

1
2
3
4
5
6
7
8
9
10
11
// BAD:类型运行时仍存在(esbuild 下可能残留)
// GOOD:
import type { User } from './types';
// 混合:
import { fetchUsers, type User } from './api';
// 仅类型:
import type { Options } from './config';
// 类型导出:
export type { ID } from './utils';
// 编译器自动 elision,显式更清晰
// 循环引用时用 import type 打破

类型化 API 封装

封装 fetch 返回强类型。响应校验、错误统一处理。泛型请求函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
async function api<T>(
url: string, opts?: RequestInit
): Promise<T> {
const res = await fetch(url, opts);
if (!res.ok) {
throw new Error(`HTTP ${res.status}`);
}
return res.json() as Promise<T>;
}
// 使用:
interface User { name: string }
const user = await api<User>('/api/user');
// 泛型让调用点类型安全
// 边界:schema 校验(zod)防运行时错位
// import { z } from 'zod';

Node HTTP 服务

node:http 或框架(Express/Fastify)。类型化请求响应。路由处理。

1
2
3
4
5
6
7
8
9
10
11
import { createServer } from 'node:http';
const server = createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ ok: true }));
});
server.listen(3000);
// Express:
// import express from 'express';
// @types/express 提供类型
// 参数类型:
// app.get('/user/:id', (req: Request<{id: string}>, res: Response) => {})

DOM 类型

document.getElementById 返回类型、事件类型、HTML 元素类型映射。

1
2
3
4
5
6
7
8
9
10
const btn = document.getElementById('btn');
// HTMLElement | null,需要非空:
if (btn) { btn.addEventListener('click', handler); }
// 断言具体元素:
const input = document.querySelector('input') as HTMLInputElement;
// 事件类型:
function handler(e: MouseEvent) { console.log(e.clientX); }
// 泛型事件:
const f = (e: KeyboardEvent) => { e.key };
// 表单取值:input.value 是 string

URL 与参数

URLSearchParams 构造查询串、URL 解析。类型安全读参数。

1
2
3
4
5
6
7
8
9
const params = new URLSearchParams({ q: 'ts', page: '2' });
params.toString(); // 'q=ts&page=2'
const url = new URL('https://example.com/search?q=ts');
const q = url.searchParams.get('q'); // 'ts'
// 构造:
url.searchParams.set('page', '3');
// 请求头:
headers.append('Authorization', `Bearer ${token}`);
// 参数类型:get 返回 string | null,收窄后使用

WebSocket

WebSocket 双向通信。onmessage 事件、泛型数据、readyState 状态。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const ws = new WebSocket('wss://example.com/socket');
// 连接就绪:
ws.onopen = () => ws.send(JSON.stringify({ type: 'join' }));
// 收消息:
ws.onmessage = (e: MessageEvent) => {
// e.data 可能是 string | Blob | ArrayBuffer
const data = JSON.parse(e.data as string);
console.log(data);
};
ws.onerror = (e) => console.error('连接错误', e);
ws.onclose = (e) => console.log('关闭', e.code);
// 主动关闭:
ws.close(1000, '正常关闭');
// 自动重连:onclose 里 setTimeout 重连

请求头与鉴权

Headers 类型安全设置。Authorization Bearer、Content-Type。拦截器统一注入。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const headers = new Headers();
headers.set('Content-Type', 'application/json');
headers.set('Authorization', `Bearer ${token}`);
const res = await fetch('/api', { headers });
// 读响应头:
const type = res.headers.get('content-type');
// 统一封装:
function authFetch(url: string, init?: RequestInit) {
return fetch(url, {
...init,
headers: {
...init?.headers,
Authorization: `Bearer ${getToken()}`,
},
});
}
// 注意:token 泄露不要放日志/URL
// 刷新:401 时刷新 token 重试

16.时间与日期

Date 对象、时间戳、格式化与时区。

Date 基础

Date 构造、getFullYear/getMonth/getDate 读取、set 系列设置。月份 0 起。

1
2
3
4
5
6
7
8
9
10
11
const now = new Date();
const d = new Date('2026-08-02T12:00:00Z');
const y = d.getFullYear(); // 2026
const m = d.getMonth(); // 7(0 起!)
const day = d.getDate(); // 2
// 设置:
d.setFullYear(2030);
d.setMonth(0); // 一月
// 本地与 UTC:
// getUTCFullYear() / getUTCHours()
// getTimezoneOffset() 分钟差

时间戳

getTime() 毫秒时间戳、Date.now()、Date.parse。日期比较用时间戳。

1
2
3
4
5
6
7
8
9
const ms = Date.now(); // 当前毫秒
const d = new Date(ms);
const older = new Date('2020-01-01');
if (d.getTime() > older.getTime()) { } // 比较
// 秒级:Math.floor(Date.now() / 1000)
// Date.parse('2026-08-02') 返回毫秒
// 加减时间:
d.setDate(d.getDate() + 7); // 加 7 天
// 注意 setMonth/setDate 跨月自动进位

格式化

toISOString UTC 格式、toLocaleDateString 本地格式、Intl.DateTimeFormat 自定义。

1
2
3
4
5
6
7
8
9
10
const d = new Date();
const iso = d.toISOString(); // '2026-08-02T04:00:00.000Z'
const local = d.toLocaleDateString('zh-CN');
// Intl 自定义:
new Intl.DateTimeFormat('zh-CN', {
year: 'numeric', month: 'long', day: 'numeric',
hour: '2-digit', minute: '2-digit',
}).format(d);
// 时间相对:
// d.toLocaleTimeString('zh-CN')

时区

时间戳是 UTC 毫秒,格式化才有本地时区。Intl 按 timeZone 选项指定时区。

1
2
3
4
5
6
7
8
9
10
11
const d = new Date();
// 指定时区:
new Intl.DateTimeFormat('zh-CN', {
timeZone: 'Asia/Shanghai',
hour12: false,
}).format(d);
// getTimezoneOffset 返回本地与 UTC 分钟差
// 存储用 ISO/时间戳,展示用本地化
// 跨时区转换:
// 存 UTC,读时 toLocaleString('zh-CN', { timeZone })
// 简单计算用 dayjs/date-fns 库

定时器

setTimeout 延时、setInterval 周期、clearTimeout 取消。返回值类型 number。

1
2
3
4
5
6
7
8
9
10
11
const timer = setTimeout(() => {
console.log('1s 后');
}, 1000);
clearTimeout(timer); // 取消
const interval = setInterval(() => {
console.log('每秒');
}, 1000);
clearInterval(interval); // 停止
// 异步等待:
await new Promise(r => setTimeout(r, 500));
// 注意:定时器回调在事件循环宏任务阶段执行

时长与区间

两个时间戳相减得毫秒时长。区间判断用时间戳比较。防滥用 setInterval 漂移。

1
2
3
4
5
6
7
8
9
10
11
const start = Date.now();
// 耗时统计:
const elapsed = Date.now() - start; // 毫秒
console.log(`${elapsed}ms`);
// 区间判断:
const inWindow = t >= start && t <= end;
// 每 N 毫秒一次(轮询):
const poll = setInterval(() => { }, 1000);
// setInterval 漂移修正:
// 用 setTimeout 递归 + 计算补偿
// 精度:performance.now() 更高

日期库

dayjs/date-fns 提供清晰 API 与时区处理。体积小、不可变。复杂时区建议用库。

1
2
3
4
5
6
7
8
9
10
11
12
13
// dayjs:
// import dayjs from 'dayjs';
// dayjs().format('YYYY-MM-DD');
// dayjs().add(7, 'day').toDate();
// dayjs('2026-08-02').isBefore('2026-09-01');
// date-fns:
// import { format, addDays, isBefore } from 'date-fns';
// format(new Date(), 'yyyy-MM-dd');
// addDays(new Date(), 7);
// 时区:
// import { formatInTimeZone } from 'date-fns-tz';
// formatInTimeZone(d, 'Asia/Shanghai', 'yyyy-MM-dd HH:mm')
// 均不可变:返回新值不修改原 Date

高精度计时

performance.now() 毫秒级高精度、不受系统时间调整影响。性能测量用。

1
2
3
4
5
6
7
8
9
10
11
12
13
const t0 = performance.now();
// 测量代码…
const elapsed = performance.now() - t0;
console.log(`${elapsed.toFixed(2)}ms`);
// 挂钟时间:Date.now() 可被修改
// performance.now() 单调递增
// 浏览器/Node 均可用
// 打点分析:
performance.mark('start');
// …
performance.mark('end');
performance.measure('任务', 'start', 'end');
// 测量结果:performance.getEntriesByName('任务')

17.进程与系统

Node 进程、命令行工具、标准流与构建产物运行。

Node 进程

process 全局对象:argv 参数、env 环境、exit 退出码、stdout/stderr 流。

1
2
3
4
5
6
7
8
9
10
import { argv, env, exit, stdout, stderr } from 'node:process';
const args = argv.slice(2);
const mode = env.NODE_ENV;
exit(0); // 成功退出
stdout.write('输出');
stderr.write('错误');
// 退出码:0 成功,1 一般错误
process.exitCode = 1; // 优雅设置
// 信号处理:
process.on('SIGINT', () => { console.log('Ctrl+C'); process.exit(0); });

命令行工具

写 CLI:解析参数、帮助输出、退出码。shebang 让脚本可执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/usr/bin/env node
const [cmd, ...rest] = process.argv.slice(2);
if (cmd === '--help' || cmd === '-h') {
console.log('用法: ts-tool <命令> [选项]');
process.exit(0);
}
if (!cmd) {
console.error('缺少命令');
process.exit(1);
}
// 参数解析库:commander / yargs
// 选项:
// ts-tool build --out dist --watch

标准流

stdin 读输入、stdout 输出、stderr 错误。readline 交互。管道数据。

1
2
3
4
5
6
7
8
9
10
11
import { stdin, stdout } from 'node:process';
import * as readline from 'node:readline/promises';
const rl = readline.createInterface({ input: stdin, output: stdout });
const name = await rl.question('名字? ');
console.log(`你好, ${name}`);
rl.close();
// 读全部 stdin:
import { readFileSync } from 'node:fs';
// 管道:echo hi | ts-tool
// 逐行处理:
for await (const line of rl) { process(line); }

运行编译产物

tsc 编译后 node dist/main.js 运行。package.json bin 注册命令。类型声明 d.ts。

1
2
3
4
5
6
7
8
9
10
11
// package.json:
// {
// "bin": { "ts-tool": "./dist/cli.js" },
// "types": "./dist/index.d.ts",
// "main": "./dist/index.js"
// }
// 编译:npx tsc
// 运行:node dist/cli.js
// 发布 npm:npm publish
// 声明文件 .d.ts 供其他 TS 项目引用
// tsconfig declaration: true

执行系统命令

execFile 执行外部命令、spawn 流式。child_process 类型。注意转义避免注入。

1
2
3
4
5
6
7
8
9
10
11
import { execFile } from 'node:child_process';
execFile('ls', ['-l'], (err, stdout) => {
if (err) { console.error(err); return; }
console.log(stdout);
});
// 流式 spawn:
import { spawn } from 'node:child_process';
const child = spawn('node', ['worker.js']);
child.stdout.on('data', (d) => console.log(d.toString()));
// 安全性:避免 exec + 字符串拼接
// 传参用数组,不拼 shell 命令

退出码

0 成功、非 0 失败。约定:1 通用错误、2 用法错误。脚本在 CI 依赖退出码。

1
2
3
4
5
6
7
8
9
process.exit(0); // 成功
process.exit(1); // 一般错误
process.exit(2); // 用法/参数错误
// 未捕获异常退出码 1
// 手动设置:
process.exitCode = 2;
// CI 中退出码非 0 即失败:
// ts-tool check && echo OK || echo FAIL
// 信号退出:SIGTERM 默认 143

npm scripts

package.json scripts 编排命令。pre/post 钩子、串联 &&、并行 &。构建链常用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// package.json:
// {
// "scripts": {
// "dev": "tsx src/dev.ts",
// "typecheck": "tsc --noEmit",
// "lint": "eslint src",
// "test": "vitest run",
// "build": "npm run typecheck && tsup src/index.ts",
// "prepublishOnly": "npm run build"
// }
// }
// 串联:&&(失败中断)
// 并行:& 或 concurrently 库
// pre/post 钩子:prebuild 在 build 前自动跑
// npx 一次性:npx vitest

配置与环境

dotenv 加载 .env、类型化配置对象、运行时校验。配置与代码分离。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 安装:npm i dotenv
// import 'dotenv/config';
const port = Number(process.env.PORT ?? 3000);
const dbUrl = process.env.DATABASE_URL;
if (!dbUrl) {
throw new Error('缺少 DATABASE_URL');
}
// 类型化配置:
interface Config {
port: number;
debug: boolean;
apiKey: string;
}
// 校验函数返回 Config,保证类型
// .env 不进版本库(gitignore)
// 环境差异:.env.development / .env.production

18.正则与文本处理

正则语法、标志、类型化匹配与文本处理惯用法。

正则语法

字面量 /.../ 或 RegExp 构造。字符类、量词、分组、锚点。

1
2
3
4
5
6
7
8
9
const re = /\b\w+@\w+\.com\b/;
// \d 数字 \w 单词 \s 空白 \b 词边界
// [abc] 字符类 [^abc] 否定
// * 零次多 + 一次多 ? 零或一 {2,4} 区间
// ^ 开头 $ 结尾
// () 分组 (?:) 非捕获
// 或:/cat|dog/
// 测试:
re.test('hi [email protected]'); // true

标志

g 全局、i 忽略大小写、m 多行、s 点通配换行、u unicode、y 粘性。

1
2
3
4
5
6
7
8
9
10
const g = /a/g; // 全局(matchAll 需要)
const i = /HELLO/i; // 忽略大小写
const m = /^line/m; // 每行匹配锚点
const s = /a.b/s; // . 匹配换行
const u = /\p{Emoji}/u; // unicode 属性
const y = /a/y; // 粘性(从 lastIndex 匹配)
// 组合:/foo/gim
// 动态构造:
new RegExp(`\\d{${len}}`, 'gi');
// 字面量必须转义 \ 字符

匹配与提取

match 返回数组(0 全匹配 + 捕获组)、matchAll 全局遍历、match 失败 null 需判空。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const s = 'id=123&id=456';
const re = /id=(\d+)/g;
// 全量匹配:
for (const m of s.matchAll(re)) {
console.log(m[1]); // '123' '456'
}
// 单次:
const first = s.match(re);
if (first) { console.log(first[0]); }
// 具名捕获:
const named = /(?<year>\d{4})-(?<month>\d{2})/;
const r = s.match(named);
if (r) { r.groups!.year; }
// 惰性 ?:/(\d+?)(x)/ 最少匹配

替换

replace 字符串/函数替换。$1 捕获引用、全局 g 全替换。函数式替换处理逻辑。

1
2
3
4
5
6
7
8
9
10
11
const s = '2026-08-02';
// 捕获引用:
const a = s.replace(/(\d{4})-(\d{2})-(\d{2})/, '$3/$2/$1');
// 函数式替换:
const b = s.replace(/(\d+)/g, (m) => String(Number(m) + 1));
// 全部替换(g 必须):
'aaa'.replace(/a/g, 'b'); // 'bbb'
// 去掉空白:
s.replace(/\s+/g, ' ').trim();
// 简单替换可用 split/join:
s.split('-').join('/');

验证惯用法

整体匹配用 ^...$ 锚定。数字/邮箱/URL 常用模式。test 返回 boolean。

1
2
3
4
5
6
7
8
9
10
11
12
13
function isEmail(v: string): boolean {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v);
}
function isInt(v: string): boolean {
return /^[-+]?\d+$/.test(v);
}
function isUrl(v: string): boolean {
try { new URL(v); return true; }
catch { return false; }
}
// 严格匹配必须 ^ 开头 $ 结尾
// 校验长度:/^.{8,20}$/
// 贪婪转义:/^\.$/ 匹配点号

正则性能

避免灾难性回溯:嵌套量词。预编译正则。g 状态 lastIndex 注意。

1
2
3
4
5
6
7
8
9
10
11
// BAD:灾难性回溯
// /^(a+)+$/ 匹配 'aaaaaaaaaaaaaaaaaaaa!' 极慢
// GOOD:避免嵌套量词
/^(a+)$/;
// 预编译避免重复创建:
const re = /\d+/g; // 模块级复用
// g 标志有状态:
re.lastIndex = 0; // 重置
// 大文本逐段处理
// 简单解析优先 indexOf / split
// 正则只做结构匹配,业务逻辑用代码

正则常见坑

字面量需转义、g 标志 lastIndex 状态、贪婪匹配、\ 在字符串里的双重转义。

1
2
3
4
5
6
7
8
9
10
11
12
13
// 字符串构造时 \ 要写两次:
new RegExp('\\d+'); // 等价 /\d+/
// g 标志有 lastIndex 状态:
const re = /a/g;
re.lastIndex = 0; // 用前重置
// 贪婪匹配:
'<a><b>'.match(/<.*>/); // 匹配到最后一个 >
'<a><b>'.match(/<.*?>/); // 非贪婪,最短
// 字面量.要转义:
/1\.0/; // 匹配 '1.0' 不是 '1X0'
// ^ 在 [] 里是取反:
/[^a]/; // 非 a
// 空匹配:/(?:)/ 匹配任意位置

常用模式

身份证/手机号/颜色/日期等常用正则片段。匹配业务格式校验。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 手机号(中国大陆):
const mobile = /^1[3-9]\d{9}$/;
// 邮箱:
const email = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
// 十六进制颜色:
const color = /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/;
// 日期 yyyy-mm-dd:
const date = /^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$/;
// URL:
const url = /^https?:\/\/[^\s]+$/;
// 中文字符:
const zh = /[\u4e00-\u9fff]/;
// 空白行:
const blank = /^\s*$/;
// 记忆要点:锚定 ^$、数量限定、字符类

19.构建与工程化

tsconfig、打包工具、Lint/格式化、测试与 CI。

tsconfig 详解

常用编译选项:moduleResolution、declaration、noUnusedLocals、esModuleInterop、paths 路径别名。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"lib": ["ES2022", "DOM"],
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"declaration": true, // 生成 .d.ts
"outDir": "dist",
"noUnusedLocals": true,
"paths": { "@/*": ["./src/*"] },
"resolveJsonModule": true
},
"include": ["src"]
}
// npm-run: tsc --noEmit 类型检查

打包工具

Vite/Webpack/Rollup 打包。Vite 是 TS/前端默认。库用 tsup/Rollup 出 ESM+CJS。

1
2
3
4
5
6
7
8
9
10
11
12
// Vite:dev server + 构建
// vite.config.ts:
export default {
build: { target: 'esnext' },
// plugins: [react(), vue()]
};
// 命令:
// npm run dev 开发
// npm run build 构建
// 库构建:tsup
// tsup src/index.ts --format esm,cjs --dts
// 环境变量:import.meta.env.VITE_XXX

Lint 与格式化

ESLint 规则检查、Prettier 格式化。ts-eslint 提供类型感知规则。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// ESLint 配置:
// {
// "parser": "@typescript-eslint/parser",
// "plugins": ["@typescript-eslint"],
// "rules": {
// "@typescript-eslint/no-explicit-any": "warn"
// }
// }
// 命令:
// npx eslint src --fix
// Prettier:
// npx prettier --write "src/**/*.ts"
// 规则模板:
// npx eslint --init
// 提交前:husky + lint-staged

测试

Vitest/Jest 单元测试。describe/it/expect。类型与运行分离。ts 直接测。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { describe, it, expect } from 'vitest';
import { add } from './add';
describe('add', () => {
it('两数相加', () => {
expect(add(1, 2)).toBe(3);
});
it('类型错误编译失败', () => {
// add('a', 2) // 编译期拦截
});
});
// 类型测试:
// import { expectType } from 'tsd';
// 覆盖率:vitest run --coverage
// Mock:vi.mock() / vi.fn()

CI 与部署

CI 阶段:类型检查、Lint、测试、构建。tsc --noEmit 拦截类型错误。

1
2
3
4
5
6
7
8
9
10
11
// GitHub Actions:
// steps:
// - run: npm ci
// - run: npx tsc --noEmit # 类型检查
// - run: npx eslint src
// - run: npm test
// - run: npm run build
// - run: npm publish --dry-run
// 类型检查放最先:失败立即中断
// 缓存:actions/cache 加速依赖
// 多版本测试:node 18/20/22 矩阵

调试

sourceMap + Node inspect 断点调试。console 调试、类型断言辅助。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// tsconfig sourceMap: true
// VS Code launch.json:
// {
// "type": "node",
// "request": "launch",
// "program": "${file}",
// "runtimeArgs": ["--loader", "tsx"]
// }
// 命令:
// node --inspect dist/main.js
// 浏览器:DevTools Sources + sourcemap
// 类型调试:
// type Debug<T> = T; hover 查看
// console 输出辅助定位

npm 发布

发布库:files 指定发布内容、version 语义化、main/types/exports 入口。发布前构建。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// package.json:
// {
// "name": "@org/my-lib",
// "version": "1.2.0",
// "main": "./dist/index.js",
// "types": "./dist/index.d.ts",
// "exports": {
// ".": { "types": "./dist/index.d.ts", "import": "./dist/index.mjs" }
// },
// "files": ["dist"],
// "sideEffects": false
// }
// 发布:
// npm run build && npm publish
// 预发布:npm publish --dry-run
// 版本:npm version patch|minor|major
// 权限:npm publish --access public

monorepo 配置

npm/pnpm workspaces 多包仓库。共享依赖、包间引用、统一脚本。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// pnpm-workspace.yaml:
// packages:
// - 'packages/*'
// - 'apps/*'
// npm workspaces:
// {
// "workspaces": ["packages/*"]
// }
// 包间引用:
// npm i @org/shared -w packages/web
// 根脚本批量:
// pnpm -r run build
// 共享 TS 配置:
// tsconfig.base.json 被各包 extends
// 依赖提升:pnpm 默认隔离,需显式声明
关于本速查

本页是 TypeScript 5.x 的自包含速查手册,覆盖类型系统与语言核心在真实项目中约 80% 的常见用法。内容偏向现代惯用法:接口与类型别名、泛型、联合/交叉类型、类型收窄、字面量类型、keyof/typeof、映射类型与条件类型,以及与异步编程的结合。TypeScript 由微软于 2012 年发布,是 JavaScript 的超集,在编译期为 JavaScript 添加静态类型检查,大幅提升大型项目的可维护性,是当前前端工程化的主流选择。 19 个章节各自聚焦一个主题:基础语法、变量与类型推断、类型系统、引用与值语义、控制流、函数与重载、字符串与模板、集合、内存与类型擦除、类与接口、错误处理、输入输出、常见误区、并发(异步类型)、网络、时间、进程、正则与构建工具(tsc/tsconfig)。每个小节都配有「概念介绍 + 可直接复制的代码片段」。 所有代码与文字均在浏览器本地渲染,无任何数据离开你的设备。权威参考见 TypeScript 官方手册。

版本 2.1.0