Skip to content

TypeScript 4.6:控制流分析的又一次飞跃

TypeScript 4.6 在 2022 年 2 月发布,最大的亮点是控制流分析的增强——现在可以在构造函数的 super() 调用之前引用 this。这个看似小的改进,解决了很多实际编码中的烦人问题。

构造函数中 super 前的 this

以前这样写会报错:

typescript
class Base {
  x: number;
  constructor(x: number) {
    this.x = x;
  }
}

class Derived extends Base {
  x: string;

  constructor(x: string) {
    // 以前:报错 —— super() 之前不能访问 this
    const normalized = x.toLowerCase();
    super(normalized.length);
    this.x = x;
  }
}

4.6 之后不再报错。关键规则:你在 super() 之前访问的 this 不是真正的 this,只是参数传递——不能读写实例属性,但可以调用方法做计算。

解构的控制流分析

typescript
type Shape =
  | { kind: "circle"; radius: number }
  | { kind: "square"; sideLength: number };

function area(shape: Shape) {
  // 4.6 之前,这样解构后 shape 的类型会丢失
  const { kind } = switch (kind) {
    // ...
  }

  // 4.6 之后,解构不会破坏控制流分析
  const { kind, ...rest } = shape;
  switch (kind) {
    case "circle":
      // rest 被正确推断为 { radius: number }
      return Math.PI * rest.radius ** 2;
    case "square":
      // rest 被正确推断为 { sideLength: number }
      return rest.sideLength ** 2;
  }
}

更好的写法:

typescript
function area(shape: Shape) {
  const { kind } = shape;
  switch (kind) {
    case "circle":
      return Math.PI * shape.radius ** 2;
    case "square":
      return shape.sideLength ** 2;
  }
}

现在 TypeScript 能理解 const { kind } = shape 之后 shape 的类型仍然有效。

递归类型引用的深度限制

typescript
// 以前:深度超过 50 就报错
// 4.6:默认深度提升到 100

type DeepNest<T, N extends number> = N extends 0
  ? T
  : DeepNest<T[], Prev<N>>;

type Prev<N extends number> = N extends 1 ? 0
  : N extends 2 ? 1
  : N extends 3 ? 2
  // ... 简化
  : never;

// 现在可以递归更深
type Result = DeepNest<string, 20>; // OK

ES2022 的 Error Cause

typescript
function parseConfig(json: string) {
  try {
    return JSON.parse(json);
  } catch (e) {
    throw new Error("配置解析失败", { cause: e });
  }
}

try {
  parseConfig("invalid json");
} catch (e) {
  console.log(e.message);           // "配置解析失败"
  console.log((e as Error).cause);  // SyntaxError: Unexpected token...
}

TypeScript 4.6 原生支持 ErrorOptions.cause,不需要额外的类型声明。

类型参数的推断改进

typescript
declare function pipe<A, B, C>(
  a: A,
  ab: (a: A) => B,
  bc: (b: B) => C
): C;

// 4.6 之前,链式调用的类型推断经常失败
const result = pipe(
  "hello",
  (s) => s.length,    // A = string, 推断 B = number
  (n) => n.toFixed(2) // B = number, 推断 C = string
);
// result: string ✅

实际项目中的收益

typescript
// 典型的 Express 中间件模式
interface Request {
  body?: unknown;
  params: Record<string, string>;
}

function validate<T>(
  handler: (req: Request & { body: T }) => void
) {
  return (req: Request) => {
    // 4.6 的控制流分析让这里的类型守卫生效
    if (!req.body) {
      throw new Error("Body is required");
    }
    handler(req as Request & { body: T });
  };
}

// 使用时,body 的类型自动推断
const createUser = validate<{ name: string; email: string }>(
  (req) => {
    console.log(req.body.name);  // string ✅
    console.log(req.body.email); // string ✅
  }
);

小结

TypeScript 4.6 的改进虽然不像 4.7 那样带来 ESM 支持的大变革,但控制流分析的增强让日常编码更顺畅。特别是解构不再破坏类型收窄,这个改进每天都能感受到。建议尽快升级,配合 strict 模式使用效果最佳。

MIT Licensed