IT

TypeScript 5 新功能 — satisfies 运算符与装饰器实战指南

USD/JPY分散は、為替急変局面で一方通貨の過大シェアを防ぎ、月次の再バランスと上限規則で感情的な一括投資を抑える実践設計です。

TypeScript 5 新功能 — satisfies 运算符与装饰器实战指南

核心要点 TypeScript 5.0~5.4 的主要新功能: ① satisfies 运算符 — 在保留推断的同时验证类型 ② 标准装饰器 — 符合 TC39 Stage 3 标准,并支持元数据 ③ const 类型参数 — 字面量类型推断更强 ④ 可扩展多个 tsconfig 档案 ⑤ enum 与 namespace 改进。实战影响最大的是 satisfies 与新的装饰器。

TypeScript 5 概览

code on a screen showing TypeScript syntax
项目数值
主要新功能satisfies 运算符、标准装饰器、const 类型参数、可扩展多个 tsconfig、enum 与 namespace 改进
实战影响最大的功能satisfies、新装饰器
主版本起始日期2023 年 3 月

TypeScript 5 是从 2023 年 3 月开始的主版本,从 5.0 到 5.4 之间累积了多次小幅更新。本版本最关键的变化,是引入了 标准装饰器satisfies 运算符

各版本主要发布时程:

版本发布主要功能
TypeScript 5.02023-03satisfies、const 类型参数、扩展多个 tsconfig
TypeScript 5.12023-06独立的 getter/setter 类型、JSX 改进
TypeScript 5.22023-08using/await using 关键字、装饰器元数据
TypeScript 5.32023-11Import Attributes、类型缩窄改进
TypeScript 5.42024-03NoInfer 工具类型、闭包中保留类型缩窄

1. satisfies 运算符 — 实战完整指南

puzzle pieces fitting together on a desk

satisfies 运算符是 TypeScript 5.0 中最具突破性的功能之一。它跳脱了传统类型断言的限制,可以 在保留类型推断的同时验证类型约束

原本的痛点

typescript
// Problem with the old approach
type Color = "red" | "green" | "blue";
type ColorMap = Record<string, string | number[]>;

// as assertion: type-check yes, inference no
const palette = {
  red: [255, 0, 0],
  green: "#00ff00",
  blue: [0, 0, 255]
} as ColorMap;

// Now palette.red is inferred as string | number[]
// You need an assertion to use .map()
palette.red.map(x => x); // Error: map does not exist on string | number[]

改用 satisfies

typescript
// satisfies operator
type Color = "red" | "green" | "blue";
type ColorMap = Record<Color, string | number[]>;

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

// Type-checked AND literal inference preserved
palette.red.map(x => x);      // OK: number[] preserved
palette.green.toUpperCase();  // OK: string preserved
const wrong = { purple: "purple" } satisfies ColorMap; // Error: "purple" is not Color

实战模式

模式 1: 验证 API 响应对象

typescript
interface ApiConfig {
  baseUrl: string;
  timeout: number;
  retries: number;
}

// satisfies for type check + precise literal inference
const config = {
  baseUrl: "https://api.example.com",
  timeout: 5000,
  retries: 3,
} satisfies ApiConfig;

// config.timeout is 5000 (literal), not just number
type TimeoutType = typeof config.timeout; // 5000

模式 2: 路由定义

typescript
type Route = {
  path: string;
  component: React.ComponentType;
  auth?: boolean;
};

const routes = [
  { path: "/", component: Home },
  { path: "/dashboard", component: Dashboard, auth: true },
] satisfies Route[];

// routes[0].path stays as literal "/" while still validated against Route

模式 3: 多语言翻译对象

typescript
type TranslationKey = "hello" | "goodbye" | "welcome";
type Translations = Record<TranslationKey, string>;

const en = {
  hello: "Hello",
  goodbye: "Goodbye",
  welcome: "Welcome",
} satisfies Translations;

const fr = {
  hello: "Bonjour",
  goodbye: "Au revoir",
  // missing welcome -> compile error
} satisfies Translations;

2. 标准装饰器 — 完整支持 TC39 Stage 3

TypeScript 5.0 支持 TC39 Stage 3 标准装饰器,这与长期使用的实验性装饰器(experimentalDecorators)是分开的两套机制。

主要差异

类别实验性装饰器标准装饰器(5.0+)
启用方式experimentalDecorators: true默认启用(无需 tsconfig 设置)
标准符合度自家实现TC39 Stage 3
执行顺序反向正向
元数据emitDecoratorMetadata使用 Symbol.metadata
函数支持有限仅 class

类装饰器

typescript
// TypeScript 5 standard decorator
function sealed(target: typeof SealedClass) {
  Object.seal(target);
  Object.seal(target.prototype);
}

@sealed
class SealedClass {
  greet() { return "Hello!"; }
}

方法装饰器

typescript
function log(target: any, context: ClassMethodDecoratorContext) {
  const methodName = String(context.name);

  return function (this: any, ...args: any[]) {
    console.log(`[${methodName}] called with`, args);
    const result = target.call(this, ...args);
    console.log(`[${methodName}] returned`, result);
    return result;
  };
}

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

const calc = new Calculator();
calc.add(1, 2);
// [add] called with [1, 2]
// [add] returned 3

存取器装饰器

typescript
function readonly(target: any, context: ClassAccessorDecoratorContext) {
  return {
    set() {
      throw new Error(`${String(context.name)} is read-only.`);
    }
  };
}

class Config {
  @readonly
  accessor version = "1.0.0";
}

const cfg = new Config();
console.log(cfg.version); // "1.0.0"
cfg.version = "2.0.0";    // Error

装饰器元数据(TypeScript 5.2)

typescript
// Using the metadata API
function validate(target: any, context: ClassFieldDecoratorContext) {
  context.metadata[context.name] = { required: true };
}

class UserForm {
  @validate
  name!: string;

  @validate
  email!: string;
}

// Reading metadata
const metadata = UserForm[Symbol.metadata];
console.log(metadata); // { name: { required: true }, email: { required: true } }

3. const 类型参数

TypeScript 5.0 新增的 const 修饰符,可强化泛型函数的类型推断。

typescript
// Old behavior
function identity<T>(value: T): T {
  return value;
}

const result = identity(["a", "b", "c"]);
// result: string[] (literal info lost)

// const type parameter
function identityConst<const T>(value: T): T {
  return value;
}

const result2 = identityConst(["a", "b", "c"]);
// result2: readonly ["a", "b", "c"] (literal preserved)

实战范例: 路由建构器

typescript
function createRoute<const Path extends string>(path: Path) {
  return { path, url: () => `https://app.example.com${path}` };
}

const homeRoute = createRoute("/");
// homeRoute.path: "/" (literal preserved)

const dashRoute = createRoute("/dashboard");
// dashRoute.path: "/dashboard" (literal preserved)

4. 扩展多个 tsconfig 档案

从 TypeScript 5.0 起,tsconfig.json 中的 extends 可以使用数组形式。

json
// tsconfig.json
{
  "extends": [
    "@tsconfig/strictest/tsconfig.json",
    "@tsconfig/node18/tsconfig.json",
    "./custom.tsconfig.json"
  ],
  "compilerOptions": {
    "outDir": "dist"
  }
}

过去需要透过链式继承多个档案,现在只要一个数组就能一次处理多重继承。

5. using / await using 关键字(TypeScript 5.2)

这是为资源管理而新增的关键字,概念上类似 Java 的 try-with-resources。

typescript
// Object with Symbol.dispose
class FileHandle {
  constructor(public name: string) {
    console.log(`open ${name}`);
  }
  [Symbol.dispose]() {
    console.log(`close ${this.name}`);
  }
}

function readData() {
  using handle = new FileHandle("data.txt");
  // do work
  return handle.name;
}
// When readData() ends, the handle is automatically disposed.

await using 则是配合异步资源(Symbol.asyncDispose)使用的版本。

6. 升级前的检查清单

升级到 TypeScript 5 之前,建议确认以下事项:

  • 调整 tsconfig.json:确认 experimentalDecorators 与新装饰器并存的策略
  • 复查现有装饰器写法:执行顺序、元数据 API 的差异
  • 升级建构工具:Webpack、Vite、SWC、esbuild 等的兼容性
  • 升级 ESLint 与 @typescript-eslint,使其能解析新语法
  • 检查所用库的类型定义档,排除不兼容的版本

参考资料: 韩国银行经济统计

常见问题 (FAQ)

Q1. TypeScript 5 中最重要的新功能是哪些?

A: 实战影响最大的是 satisfies 运算符、标准装饰器与 const 类型参数。

Q2. 应该在什么场合使用 satisfies 运算符?

A: 当你想验证一个对象符合特定类型,又同时希望保留它的精确推断结果时使用。

Q3. satisfies 和 as 有什么差别?

A: as 属于类型断言,satisfies 属于类型验证,因此在结构错误时更能安全地捕获错误。

Q4. TypeScript 5 的装饰器和旧版有何不同?

A: 标准装饰器遵循 TC39 流程,行为与原本的 experimentalDecorators 不同。

Q5. const 类型参数为何有用?

A: 它能在泛型函数中保留字面量类型推断,让你能写出更精确的类型。

Q6. 升级到 TypeScript 5 之前要检查什么?

A: tsconfig、装饰器写法、建构工具、ESLint,以及所用库的类型兼容性。

🔧 Related Free Tools

相关