← Programming Core

TYPESCRIPT CORE · 94

Function Types 與 Generics:描述「型別之間的關係」

Generic 不是看起來很進階的尖括號。它真正用途是:某個 function 可以處理多種型別,但 input / output 之間仍有必須保持的關係。

Learning outcomes

1. Function type 是 contract

function formatScore(score: number): string {
  return score.toFixed(1);
}

caller 必須提供 number;function 保證回 string。這個 contract 會一路傳到呼叫端。

2. Callback 也有 type

type CourseMapper =
  (course: Course) => string;

function mapCourses(
  courses: Course[],
  mapper: CourseMapper
): string[] {
  return courses.map(mapper);
}

TypeScript 不只是檢查「這是不是 function」,還能檢查 callback input / output 是否符合需求。

3. Generic:未知,但不是 any

function first<T>(
  items: T[]
): T | undefined {
  return items[0];
}

const n = first([1, 2, 3]);
// T = number

const c = first([
  { id: 1, title: "JS", score: 90 }
]);
// T = Course-like object
關鍵:T 不是「隨便什麼都可以然後不檢查」,而是「呼叫這次決定了一個型別,input/output 都要保持這個關係」。

4. Constraint:generic 仍可要求最低能力

function findById<T extends { id: number }>(
  items: T[],
  id: number
): T | undefined {
  return items.find(item => item.id === id);
}

任何 T 都可以,只要至少有 number 型別的 id。

5. Generic Result:統一 success / error contract

type Result<T> =
  | { ok: true; data: T }
  | { ok: false; error: string };

function success<T>(data: T): Result<T> {
  return { ok: true, data };
}

這比 any 好,因為成功時 data 的具體型別會一路保留下來。

6. Project checkpoint:Typed Course API Client v4

type Result<T> =
  | { ok: true; data: T }
  | { ok: false; error: string };

async function requestJson<T>(
  url: string,
  validate: (value: unknown) => value is T
): Promise<Result<T>> {
  try {
    const response = await fetch(url);

    if (!response.ok) {
      return {
        ok: false,
        error: "HTTP " + response.status
      };
    }

    const raw: unknown = await response.json();

    if (!validate(raw)) {
      return {
        ok: false,
        error: "Invalid payload"
      };
    }

    return {
      ok: true,
      data: raw
    };
  } catch {
    return {
      ok: false,
      error: "Network failure"
    };
  }
}

這裡 generic 沒有取代 validation;它把「validator 通過後,Result 裡的 data 就是 T」這個關係表達出來。

Knowledge check

  1. first<T> 和回傳 any 的差別是什麼?
  2. 為什麼 findById 要 constraint?
  3. 替 requestJson 寫一個 isCourseArray validator。
  4. 說明 generic 與 runtime validation 各自負責哪一層。