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