☰
The Concise TypeScript Book:一次性掌握 TypeScript 全部实用特性与进阶语法
2026/9/26 2:39:41 网站建设 项目流程
  • 文档
  • 教程

【免费下载链接】typescript-book

The Concise TypeScript Book: A Concise Guide to Effective Development in TypeScript. Free and Open Source.

项目地址:https://gitcode.com/gh_mirrors/typ/typescript-book
点击查看免费下载

本篇指南以开源仓库 The Concise TypeScript Book(GitHub 加速计划 / typ / typescript-book)中「Others / 其他特性」章节(英文原文位于 website/src/content/docs/book/others.md,意大利语译文位于 website/src/content/docs/it-it/book/others.md)为骨架,系统梳理 TypeScript 中散落在各主题之外、但在日常开发中极其常用的 26 项语言特性与工程实践:从错误处理、Mixin、异步迭代,到satisfies、可折叠元组、显式资源管理与import defer等最新语法。读完本文,你将掌握这些特性的类型语义、正确用法与对应的tsconfig.json配置,并理解它们在真实项目(包括本书仓库本身的工具链)中的落地点。

一、错误与异常处理

TypeScript 使用 JavaScript 标准的错误处理机制来捕获和处理错误。

Try-Catch-Finally 块

try { // 可能抛出错误的代码 } catch (error) { // 处理错误 } finally { // 始终会执行的代码,finally 是可选的 }

区分不同类型的错误

同一个try块可能抛出不同类型的错误,可以用instanceof分别处理:

try { // 可能抛出不同类型错误的代码 } catch (error) { if (error instanceof TypeError) { // 处理 TypeError } else if (error instanceof RangeError) { // 处理 RangeError } else { // 处理其他错误 } }

自定义错误类型

通过继承Error类可以定义更具体的错误类型,为调用方提供可编程判断的错误语义:

class CustomError extends Error { constructor(message: string) { super(message); this.name = 'CustomError'; } } throw new CustomError('Questo è un errore personalizzato.');

注意this.name的显式赋值:它是后续用error.name或error instanceof CustomError区分错误的关键。本章配套内容可继续阅读 类型收窄与守卫,结合类型谓词让错误处理更具类型安全性。

二、Mixin 类:组合而非深继承

Mixin 类允许把多个类的行为组合进一个类中,实现功能复用与扩展,而无需维护深层的继承链。TypeScript 没有内建「mixin」关键字,但可以用「类 + 接口合并 + 运行时属性复制」三件套实现:

abstract class Identificabile { name: string = ''; logId() { console.log('id:', this.name); } } abstract class Selezionabile { selected: boolean = false; select() { this.selected = true; console.log('Select'); } deselect() { this.selected = false; console.log('Deselect'); } } class MyClass { constructor() {} } // 声明 MyClass 同时具有 Identificabile 与 Selezionabile 的行为 interface MyClass extends Identificabile, Selezionabile {} // 将各基类的原型方法复制到目标类 function applyMixins(source: any, baseCtors: any[]) { baseCtors.forEach(baseCtor => { Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => { const descriptor = Object.getOwnPropertyDescriptor( baseCtor.prototype, name ); if (descriptor) { Object.defineProperty(source.prototype, name, descriptor); } }); }); } // 把 mixin 应用到 MyClass applyMixins(MyClass, [Identificabile, Selezionabile]); let o = new MyClass(); o.name = 'abc'; o.logId(); o.select();

原理拆解:

  • 接口与类同名合并(declaration merging)让编译器相信MyClass拥有 mixin 成员,提供类型安全;
  • applyMixins在运行时通过Object.getOwnPropertyNames与Object.defineProperty把原型方法实际复制到目标类原型上,两者缺一不可。

三、异步语言特性

TypeScript 是 JavaScript 的超集,天然内建了以下异步语言特性。

Promise

Promise 通过.then()与.catch()处理异步操作的成功与失败结果。

async / await

async用于声明异步函数,await用于在异步函数内部暂停执行,直到 Promise 被 resolve 或 reject,从而获得更接近同步的写法。

TypeScript 对以下 Web API 有良好的类型支持:Fetch API、Web Workers、Shared Workers、WebSocket。

四、迭代器与生成器

TypeScript 对迭代器(Iterator)与生成器(Generator)均有良好支持。

迭代器

迭代器是实现迭代器协议的对象:它持有一个指向序列下一元素的指针,提供next()方法返回下一个值及表示序列是否done的布尔值:

class NumberIterator implements Iterable<number> { private current: number; constructor( private start: number, private end: number ) { this.current = start; } public next(): IteratorResult<number> { if (this.current <= this.end) { const value = this.current; this.current++; return { value, done: false }; } else { return { value: undefined, done: true }; } } [Symbol.iterator](): Iterator<number> { return this; } } const iterator = new NumberIterator(1, 3); for (const num of iterator) { console.log(num); }

注意两点:[Symbol.iterator]()返回this使对象自身可迭代(for...of可用);当序列耗尽时返回{ value: undefined, done: true }。

生成器

生成器是用function*定义的特殊函数,用yield定义值序列,并在请求值时自动暂停与恢复执行,大大简化迭代器的编写,尤其适合处理大型或无限序列:

function* numberGenerator(start: number, end: number): Generator<number> { for (let i = start; i <= end; i++) { yield i; } } const generator = numberGenerator(1, 5); for (const num of generator) { console.log(num); }

TypeScript 还支持异步迭代器与异步生成器(配合下文for-await-of使用)。

五、JSDoc 类型标注(TsDocs)

在 JavaScript 代码库中工作时,可以用带类型注解的 JSDoc 注释帮助 TypeScript 推断正确类型:

/** * Calcola la potenza di un numero dato * @constructor * @param {number} base – Il valore base dell'espressione * @param {number} exponent – Il valore esponente dell'espressione */ function power(base: number, exponent: number) { return Math.pow(base, exponent); } power(10, 2); // function power(base: number, exponent: number): number

要点:

  • JSDoc 标签(如@param、@returns、@type)会被 TypeScript 作为类型信息来源;
  • 从 TypeScript 3.7 起,可以从 JavaScript JSDoc 语法直接生成.d.ts类型定义文件,实现「先有 JS、后补类型」的渐进式迁移。

六、@types:为纯 JavaScript 库补充类型

@types组织下的包是特殊的命名约定,用于为现有 JavaScript 库或模块提供类型定义。例如:

npm install --save-dev @types/lodash

会在当前项目安装lodash的类型定义(放在node_modules/@types下,TypeScript 会自动发现)。仓库自身的工具链也依赖了这一生态——tools/package.json 中的@types/fs-extra、@types/marked、@types/node、@types/prettier即为典型使用。想要为@types包贡献类型定义,可向 DefinitelyTyped 提交 pull request。

七、JSX

JSX(JavaScript XML)是 JavaScript 语言语法的扩展,允许在 JS/TS 文件中编写类似 HTML 的代码,常用于 React 定义 HTML 结构。TypeScript 通过类型检查与静态分析扩展了 JSX 的能力。

使用 JSX 需要在tsconfig.json中设置jsx编译选项,两个常用配置:

  • "preserve":原样输出.jsx文件,把 JSX 转换交给 Babel 等独立工具;
  • "react":启用 TypeScript 内建转换,将使用React.createElement。

八、ES6 模块与编译目标配置

TypeScript 支持 ES6(ECMAScript 2015)及之后的多个版本,因此可以直接使用箭头函数、模板字面量、类、模块、解构等语法。启用 ES6 特性需在tsconfig.json中指定target:

{ "compilerOptions": { "target": "es6", "module": "es6", "moduleResolution": "node", "sourceMap": true, "outDir": "dist" }, "include": ["src"] }

字段作用:module决定模块系统,moduleResolution决定模块解析策略,sourceMap生成调试用 source map,outDir指定输出目录,include限定编译入口。

九、ES7 幂运算符

幂运算符(**)计算第一个操作数作为底数、第二个作为指数的幂,功能类似Math.pow(),且额外支持 BigInt 操作数。当target为es2016或更高时完全支持:

console.log(2 ** (2 ** 2)); // 16

十、for-await-of 语句

该语句用于遍历异步可迭代对象,target需为es2018及以上:

async function* asyncNumbers(): AsyncIterableIterator<number> { yield Promise.resolve(1); yield Promise.resolve(2); yield Promise.resolve(3); } (async () => { for await (const num of asyncNumbers()) { console.log(num); } })();

这是「异步生成器 + 异步迭代」组合的典型用法,常用于流式处理。

十一、new.target 元属性

new.target可判断函数或构造器是否被new运算符调用,从而检测对象是否由构造器调用创建:

class Parent { constructor() { console.log(new.target); // 记录用于创建实例的构造函数 } } class Child extends Parent { constructor() { super(); } } const parentX = new Parent(); // [Function: Parent] const child = new Child(); // [Function: Child]

可以看到new.target在不同调用链下指向不同的构造函数,是区分直接实例化与继承实例化的利器。

十二、动态导入表达式

借助 ECMAScript 动态导入提案,可以条件加载模块或按需延迟加载:

async function renderWidget() { const container = document.getElementById('widget'); if (container !== null) { const widget = await import('./widget'); // 动态导入 widget.render(container); } } renderWidget();

await import()返回模块命名空间对象,适用于代码分割、按路由懒加载等场景。更进阶的模块加载语义见下文「Import Attributes」与「import defer」。

十三、tsc --watch

tsc --watch启动带--watch参数的编译器,在 TypeScript 文件被修改时自动重新编译:

tsc --watch

从 TypeScript 4.9 起,文件监视主要依赖文件系统事件;当无法建立基于事件的 watcher 时自动回退到轮询(polling)模式。

十四、非空断言运算符

非空断言运算符(后缀!,也称作明确赋值断言)允许断言变量或属性非null/undefined,即使静态类型分析提示它可能为空,从而去掉显式检查:

type Person = { name: string; }; const printName = (person?: Person) => { console.log(`Name is ${person!.name}`); };

!只影响类型检查,不产生任何运行时效果——运行时若值确实为null/undefined依然会抛错,应只在确信非空时使用。

十五、默认值声明

当变量或参数被赋默认值时使用默认值声明;若未提供值,则使用默认值:

function greet(name: string = 'Anonymous'): void { console.log(`Ciao, ${name}!`); } greet(); // Ciao, Anonymous! greet('John'); // Ciao, John!

默认参数参与类型推断:name的声明类型string与默认值'Anonymous'一致,因此调用时可以安全省略参数。

十六、可选链

可选链运算符?.与普通点运算符.类似地访问属性或方法,但对null/undefined优雅处理:终止表达式并返回undefined,而不是抛错:

type Person = { name: string; age?: number; address?: { street?: string; city?: string; }; }; const person: Person = { name: 'John', }; console.log(person.address?.city); // undefined

多级访问(如a?.b?.c)可以串联使用,是安全访问嵌套可选结构的标配。

十七、空值合并运算符

??在左侧为null或undefined时返回右侧值,否则返回左侧值。注意它与||的关键差异:??只对null/undefined兜底,不会把0、''、false当成「假值」替换掉:

const foo = null ?? 'foo'; console.log(foo); // foo const baz = 1 ?? 'baz'; const baz2 = 0 ?? 'baz'; console.log(baz); // 1 console.log(baz2); // 0 // 0 不是 null/undefined,因此保留

十八、模板字面量类型

模板字面量类型允许在类型层面操作字符串值,基于现有字符串类型生成新的字符串类型,用于构建更具表现力和精确性的类型:

type Department = 'engineering' | 'hr'; type Language = 'english' | 'spanish'; type Id = `${Department}-${Language}-id`; // "engineering-english-id" | "engineering-spanish-id" | "hr-english-id" | "hr-spanish-id"

Id是四种组合字符串的联合类型。结合下文「模板字符串模式索引签名」可在类型层面建模「路径-资源」之类的键空间。

十九、函数重载

函数重载允许为同一函数名定义多个函数签名,每个签名具有不同的参数类型与返回类型。调用重载函数时,TypeScript 依据提供的实参决定使用哪个签名:

function makeGreeting(name: string): string; function makeGreeting(names: string[]): string[]; function makeGreeting(person: unknown): unknown { if (typeof person === 'string') { return `Ciao ${person}!`; } else if (Array.isArray(person)) { return person.map(name => `Ciao, ${name}!`); } throw new Error('Impossibile salutare'); } makeGreeting('Simon'); makeGreeting(['Simone', 'John']);

实现签名(unknown版本)对调用方不可见,只负责运行时逻辑;重载列表必须与实现兼容,否则编译报错。

二十、递归类型

递归类型是可以引用自身的类型,适用于定义具有层级或递归结构(可能无限嵌套)的数据结构,如链表、树、图:

type ListNode<T> = { data: T; next: ListNode<T> | undefined; };

借助| undefined终止递归,避免类型上的无限展开。

二十一、递归条件类型

可以用逻辑与递归定义复杂的类型关系,分两步理解:

条件类型——根据布尔条件定义类型:

type CheckNumber<T> = T extends number ? 'Number' : 'Not a number'; type A = CheckNumber<123>; // 'Number' type B = CheckNumber<'abc'>; // 'Not a number'

递归——类型定义在自身内部引用自己:

type Json = string | number | boolean | null | Json[] | { [key: string]: Json }; const data: Json = { prop1: true, prop2: 'prop2', prop3: { prop4: [], }, };

递归条件类型则把二者结合:类型定义通过条件逻辑依赖自身,创建复杂而灵活的类型关系:

type Flatten<T> = T extends Array<infer U> ? Flatten<U> : T; type NestedArray = [1, [2, [3, 4], 5], 6]; type FlattenedArray = Flatten<NestedArray>; // 2 | 3 | 4 | 5 | 1 | 6

Flatten用infer U取出数组元素类型并递归展开,最终得到所有叶子元素的联合类型。

二十二、Node 中的 ECMAScript 模块支持

Node.js 自 15.3.0 起支持 ECMAScript 模块,TypeScript 自 4.7 起为 Node.js 提供对应支持。在tsconfig.json中设置module为nodenext即可启用:

{ "compilerOptions": { "module": "nodenext", "outDir": "./lib", "declaration": true } }

配套规则:

  • Node.js 支持两种模块文件扩展名:.mjs(ES 模块)与.cjs(CommonJS);TypeScript 对应为.mts与.cts,编译器转译后会生成.mjs/.cjs;
  • 在package.json中设置"type": "module"可让 Node.js 把整个项目当作 ES 模块项目;
  • TypeScript 同样支持.d.ts声明文件,为库提供类型检查与自动补全信息。

二十三、断言函数

断言函数根据返回值表明某个特定条件是否成立。最简单形式下,断言函数检查谓词,当谓词为假时抛出错误:

function isNumber(value: unknown): asserts value is number { if (typeof value !== 'number') { throw new Error('Not a number'); } }

也可以声明为函数表达式:

type AssertIsNumber = (value: unknown) => asserts value is number; const isNumber: AssertIsNumber = value => { if (typeof value !== 'number') { throw new Error('Not a number'); } };

断言函数与类型守卫(type guard)相似但目的不同:

  • 类型守卫:返回布尔值指示谓词真假,供if分支收窄类型;
  • 断言函数:谓词不满足时抛出错误而非返回false,用于「此处必须满足某条件,否则终止」的校验场景。

类型守卫示例:

const isNumber = (value: unknown): value is number => typeof value === 'number';

二十四、可变元组类型(Variadic Tuple Types)

可变元组类型是 TypeScript 4.0 引入的特性。先回顾元组:长度确定、每个元素类型已知的数组:

type Student = [string, number]; const [name, age]: Student = ['Simone', 20];

「可变(variadic)」指不定元(接受数量可变的参数)。可变元组具有普通元组的所有属性,但精确形状尚未确定,用泛型展开占位:

type Bar<T extends unknown[]> = [boolean, ...T, number]; type A = Bar<[boolean]>; // [boolean, boolean, number] type B = Bar<['a', 'b']>; // [boolean, 'a', 'b', number] type C = Bar<[]>; // [boolean, number]

元组形状由传入的泛型T决定。可变元组还可以接受多个泛型,非常灵活:

type Bar<T extends unknown[], G extends unknown[]> = [...T, boolean, ...G]; type A = Bar<[number], [string]>; // [number, boolean, string] type B = Bar<['a', 'b'], [boolean]>; // ["a", "b", boolean, boolean]

新特性带来的能力:

  • 元组类型语法中的 spread 现在可以是泛型,即使不知道实际操作的类型,也能表示对元组和数组的高阶操作;
  • rest 元素可以出现在元组任意位置。

示例——拼接任意长度元组:

type Items = readonly unknown[]; function concat<T extends Items, U extends Items>( arr1: T, arr2: U ): [...T, ...U] { return [...arr1, ...arr2]; } concat([1, 2, 3], ['4', '5', '6']); // [1, 2, 3, "4", "5", "6"]

concat的返回类型由两个实参元组精确推导,兼具类型安全与灵活性。

二十五、Boxed 类型(包装对象)

Boxed 类型指用于把原始类型表示为对象的包装对象。这些包装对象提供原始值上不直接可用的额外功能与方法。

访问string原始值上的charAt或normalize等方法时,JavaScript 会把它包装成String对象、调用方法、随后丢弃该对象。演示:

const originalNormalize = String.prototype.normalize; String.prototype.normalize = function () { console.log(this, typeof this); return originalNormalize.call(this); }; console.log('\u0041'.normalize());

TypeScript 通过为原始类型和对应包装对象提供不同类型来体现这种差异:

  • string => String
  • number => Number
  • boolean => Boolean
  • symbol => Symbol
  • bigint => BigInt

Boxed 类型通常并不需要,应避免使用,改用原始类型,例如用string而非String。

二十六、TypeScript 中的协变与逆变

协变与逆变描述泛型类型中类型关系的行为方式。在 TypeScript 中:

  • 数组是协变的,但这并非完全类型安全;
  • 函数参数类型:启用strictFunctionTypes时为逆变,否则为双变(bivariant)。

协变表示关系被保留:若类型 A 是类型 B 的子类型,则F<A>也是F<B>的子类型。TypeScript 中常见于返回类型与数组(虽然数组协变不完全类型安全)。

逆变表示关系被反转:若类型 A 是类型 B 的子类型,则F<B>是F<A>的子类型。函数参数类型被设计为逆变:接受更宽泛类型的函数可用于期望更具体类型的位置。

实践中,TypeScript 常对函数参数允许双变(除非启用strictFunctionTypes),即两个方向都可能被接受,即便不是严格类型安全。

类比:想象一个容纳所有动物的空间,和一个只容纳狗的空间。

  • 协变:在期望「动物空间」的地方可以使用「狗空间」,因为所有狗都是动物;但反之不行,因为「动物空间」可能包含非狗动物。
  • 逆变(以函数视角):如果你有能处理任何动物的东西,就可以用在期望只处理狗的地方;反过来不行。

协变示例(数组):

class Animal { name: string; constructor(name: string) { this.name = name; } } class Dog extends Animal { breed: string; constructor(name: string, breed: string) { super(name); this.breed = breed; } } let animals: Animal[] = []; let dogs: Dog[] = []; // 数组在 TypeScript 中是协变的(但并不类型安全) animals = dogs; // 允许 dogs = animals; // 错误

逆变示例(函数参数):

class Animal { name: string; constructor(name: string) { this.name = name; } } class Dog extends Animal { breed: string; constructor(name: string, breed: string) { super(name); this.breed = breed; } } type Feed<T> = (animal: T) => void; let feedAnimal: Feed<Animal> = animal => { console.log(animal.name); }; let feedDog: Feed<Dog> = dog => { console.log(dog.breed); }; // 预期的逆变: feedDog = feedAnimal; // 安全 // 取决于编译器设置: feedAnimal = feedDog; // 仅在启用 strictFunctionTypes 时报错

strictFunctionTypes是strict家族的一员(本仓库工具链 tools/tsconfig.json 中"strict": true即开启该行为),开启后方法参数类型才按严格的逆变检查。

类型参数的方差标注(可选)

从 TypeScript 4.7.0 起,可以使用out与in关键字标注方差。协变用out:

type AnimalCallback<out T> = () => T; // 此时 T 是协变的

逆变用in:

type AnimalCallback<in T> = (value: T) => void; // 此时 T 是逆变的

二十七、模板字符串模式索引签名

模板字符串模式索引签名允许用模板字符串模式定义灵活的索引签名,从而创建可按特定字符串键模式索引的对象,访问和操作属性时获得更多控制与精确性。TypeScript 自 4.4 起允许为 symbol 与模板字符串模式提供索引签名:

const uniqueSymbol = Symbol('description'); type MyKeys = `key-${string}`; type MyObject = { [uniqueSymbol]: string; [key: MyKeys]: number; }; const obj: MyObject = { [uniqueSymbol]: 'Chiave simbolo univoca', 'key-a': 123, 'key-b': 456, }; console.log(obj[uniqueSymbol]); // Chiave simbolo univoca console.log(obj['key-a']); // 123 console.log(obj['key-b']); // 456

[key: MyKeys]: number只允许key-前缀的键,比[key: string]的泛化签名精确得多。

二十八、satisfies 运算符

satisfies运算符用于检查给定类型是否满足特定接口或条件,即确保类型具备某接口要求的全部属性与方法,同时保留字面量类型的精确推断。示例:

type Columns = 'name' | 'nickName' | 'attributes'; type User = Record<Columns, string | string[] | undefined>; // 使用 `User` 做类型标注 const user: User = { name: 'Simone', nickName: undefined, attributes: ['dev', 'admin'], }; // 下面几行中,TypeScript 无法正确推断 user.attributes?.map(console.log); // 属性 'map' 在类型 'string | string[]' 上不存在。属性 'map' 在类型 'string' 上不存在。 user.nickName; // string | string[] | undefined // 使用 `as` 做类型断言 const user2 = { name: 'Simon', nickName: undefined, attributes: ['dev', 'admin'], } as User; // 这里同样无法正确推断 user2.attributes?.map(console.log); // 属性 'map' 在类型 'string | string[]' 上不存在。属性 'map' 在类型 'string' 上不存在。 user2.nickName; // string | string[] | undefined // 使用 `satisfies` 运算符,现在可以正确推断类型 const user3 = { name: 'Simon', nickName: undefined, attributes: ['dev', 'admin'], } satisfies User; user3.attributes?.map(console.log); // TypeScript 正确推断为: string[] user3.nickName; // TypeScript 正确推断为: undefined

对比可见:user: User与user2 as User都会让attributes丢失精确类型;而satisfies User既完成结构校验,又保留attributes为string[]、nickName为undefined的精确推断——这是它被广泛推荐替代as的原因。

二十九、纯类型导入与导出(type-only)

纯类型导入导出允许只导入/导出类型,而不导入与其关联的值或函数,有助于减小打包体积:

import type { House } from './house.ts';

TypeScript 允许在纯类型导入中使用声明文件与实现文件扩展名(.ts、.mts、.cts、.tsx),不受allowImportingTsExtensions设置影响。支持的形式:

import type T from './mod'; import type { A, B } from './mod'; import type * as Types from './mod'; export type { T }; export type { T } from './mod';

三十、using 声明与显式资源管理

using声明是一种块级作用域、不可变的绑定,类似const,用于管理一次性资源。用值初始化时,该值的Symbol.dispose方法会被登记,并在离开外层块作用域时执行。它基于 ECMAScript 的资源管理特性,适用于对象创建后的必要清理任务,如关闭连接、删除文件、释放内存。

注意事项:

  • 由于该特性于 TypeScript 5.2 才引入,多数运行时没有原生支持,需要为Symbol.dispose、Symbol.asyncDispose、DisposableStack、AsyncDisposableStack、SuppressedError提供 polyfill;
  • 需在tsconfig.json中作如下配置:
{ "compilerOptions": { "target": "es2022", "lib": ["es2022", "esnext.disposable", "dom"] } }

示例:

//@ts-ignore Symbol.dispose ??= Symbol('Symbol.dispose'); // 简易 polyfill const doWork = (): Disposable => { return { [Symbol.dispose]: () => { console.log('disposed'); }, }; }; console.log(1); { using work = doWork(); // 声明资源 console.log(2); } // 资源被释放(例如求值 `work[Symbol.dispose]()`) console.log(3);

输出:

1 2 disposed 3

可释放资源必须实现Disposable接口:

// lib.esnext.disposable.d.ts interface Disposable { [Symbol.dispose](): void; }

using声明把资源释放操作记录在栈中,确保按声明的逆序释放:

{ using j = getA(), y = getB(); using k = getC(); } // 依次释放 C、B、A

即使后续代码或异常发生,资源也保证会被释放。这可能导致释放时抛出异常,从而可能压制另一个异常;为保留被压制错误的信息,引入了新的原生异常SuppressedError。

await using 声明

await using声明管理异步释放的资源。值必须具有Symbol.asyncDispose方法,该方法将在块结束时被 await:

async function doWorkAsync() { await using work = doWorkAsync(); // 声明资源 } // 资源被释放(例如求值 `await work[Symbol.asyncDispose]()`)

异步可释放资源须实现Disposable或AsyncDisposable接口:

// lib.esnext.disposable.d.ts interface AsyncDisposable { [Symbol.asyncDispose](): Promise<void>; }

完整示例——异步关闭数据库连接:

//@ts-ignore Symbol.asyncDispose ??= Symbol('Symbol.asyncDispose'); // 简易 polyfill class DatabaseConnection implements AsyncDisposable { // 对象被异步释放时调用的方法 [Symbol.asyncDispose]() { // 关闭连接并返回 promise return this.close(); } async close() { console.log('Chiusura della connessione...'); await new Promise(resolve => setTimeout(resolve, 1000)); console.log('Connessione chiusa.'); } } async function doWork() { // 创建新连接,并在离开作用域时异步释放 await using connection = new DatabaseConnection(); // 声明资源 console.log('Sto lavorando...'); } // 资源被释放(例如求值 `await connection[Symbol.asyncDispose]()`) doWork();

输出:

Sto lavorando... Chiusura della connessione... Connessione chiusa.

using与await using声明允许出现在以下语句中:for、for-in、for-of、for-await-of、switch。这一特性在仓库工具链中已有落地——tools/tsconfig.json 的lib包含"esnext.disposable",说明本书构建脚本已按该能力配置编译环境。

三十一、导入属性(Import Attributes)

TypeScript 5.3 的导入属性(导入标签)告知运行时如何处理模块(如 JSON 等)。它通过保证导入清晰来提升安全性,并与内容安全策略(CSP)对齐,实现更安全的资源加载。TypeScript 只确保其合法性,模块的特定处理解释交由运行时完成:

import config from './config.json' with { type: 'json' };

与动态导入结合:

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

三十二、正则表达式语法检查

自 TypeScript 5.5.4 起,编译器会在编译期检查正则字面量的常见错误(如非法语法、错误的反向引用、目标 JS 版本不支持的特性),从而更早发现 bug;但它不检查new RegExp("...")字符串形式:

let r = /(a)\2/; // 错误:此反向引用指向一个不存在的分组。

三十三、import defer

import defer允许加载模块但延迟其执行,直到实际使用模块中的某样东西才真正运行模块代码,从而避免不必要的工作与副作用:

  • 仅支持import defer * as name from "module"形式;
  • 代码仅在访问某个导出项时才执行。

它与动态导入、import type一起,构成了 TypeScript 在现代模块加载语义上的完整工具箱。

结语

「Others」章节看似零散,实则是 TypeScript 在生产环境中的高频工具箱:错误处理与断言函数保障运行期正确性,Mixin、协变/逆变与可变元组塑造类型系统表现力,satisfies、模板字面量类型与递归条件类型把「类型即文档」推向极致,而using/await using、导入属性、import defer与正则语法检查则代表了语言最新的演进方向。这些特性彼此配合,覆盖了从异常捕获到模块加载的完整开发链路;配合本书仓库中的 完整目录 与其他章节(如 类型谓词),即可在真实项目中按需取用。

  • 文档
  • 教程

【免费下载链接】typescript-book

The Concise TypeScript Book: A Concise Guide to Effective Development in TypeScript. Free and Open Source.

项目地址:https://gitcode.com/gh_mirrors/typ/typescript-book
点击查看免费下载
上一篇:ntscQT终极指南:为你的视频添加真实VHS复古效果的完整教程
下一篇:GTA模组管理革命:Mod Loader实战指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询