Skip to content
⚠️ This article was written in 2022. Some content may be outdated.

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