0Pricing
TypeScript Academy · บทเรียน

ความลึกและขีดจำกัดของการเรียกซ้ำ

ทำความเข้าใจข้อจำกัดด้านความลึกของการเรียกซ้ำใน TypeScript

ความลึกและขีดจำกัดของการเรียกซ้ำ เป็นบทเรียน TypeScript Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน TypeScript Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส TypeScript Academy มีบทเรียนทั้งหมด 4 บทเรียน

TypeScript มีขีดจำกัดการเรียกซ้ำ

ชนิดข้อมูลแบบเรียกซ้ำมีประสิทธิภาพสูง แต่ตัวตรวจสอบชนิดข้อมูลจะจำกัดความลึกที่สามารถสร้างอินสแตนซ์ได้ หากถึงขีดจำกัด คุณจะได้รับข้อผิดพลาดด้านความลึก

// Excessively deep recursive types can produce:
// "Type instantiation is excessively deep and possibly infinite."

ที่มาของข้อผิดพลาดด้านความลึก

ชนิดข้อมูลแบบมีเงื่อนไขที่เรียกซ้ำโดยไม่มีกรณีฐานที่ชัดเจนและมีขนาดเล็กลง อาจวนซ้ำรุนแรง โดยเฉพาะเมื่อใช้ร่วมกับยูเนียนขนาดใหญ่หรือการสร้างทูเพิล

type Repeat<N extends number, Acc extends unknown[] = []> =
  Acc["length"] extends N ? Acc : Repeat<N, [...Acc, unknown]>;
// Large N can exceed the instantiation depth.
type Five = Repeat<5>;

การเรียกซ้ำที่จำกัดขอบเขตอย่างปลอดภัย

ทำให้การเรียกซ้ำมีความตื้นและอยู่ภายในขอบเขต การสร้างทูเพิลขนาดเล็กทำได้อย่างปลอดภัย โดยกรณีฐาน Acc["length"] extends N จะหยุดการเรียกซ้ำได้อย่างรวดเร็ว

type Tuple<N extends number, Acc extends unknown[] = []> =
  Acc["length"] extends N ? Acc : Tuple<N, [...Acc, unknown]>;
type Three = Tuple<3>; // [unknown, unknown, unknown]
const t: Three = [1, 2, 3];
console.log(t.length);

รูปแบบชนิดข้อมูลแบบเรียกซ้ำหาง

TypeScript ปรับปรุงประสิทธิภาพของชนิดข้อมูลแบบมีเงื่อนไขที่เรียกซ้ำแบบหางบางรูปแบบด้วยการส่งต่อตัวสะสม ทำให้เรียกซ้ำได้ลึกกว่าการซ้อนกันแบบพื้นฐาน

type Reverse<T extends unknown[], Acc extends unknown[] = []> =
  T extends [infer Head, ...infer Tail]
    ? Reverse<Tail, [Head, ...Acc]>
    : Acc;
type R = Reverse<[1, 2, 3]>; // [3, 2, 1]
const r: R = [3, 2, 1];
console.log(r);

เหตุผลที่ตัวสะสมช่วยได้

การส่งผลลัพธ์ต่อไปในตัวสะสมจะทำให้การเรียกซ้ำอยู่ในตำแหน่งหาง ซึ่ง TypeScript สามารถคลี่การเรียกซ้ำได้อย่างมีประสิทธิภาพมากกว่าชนิดข้อมูลแบบมีเงื่อนไขที่ซ้อนกันลึก

type Join<T extends string[], Acc extends string = ""> =
  T extends [infer H extends string, ...infer R extends string[]]
    ? Join<R, Acc extends "" ? H : Acc>
    : Acc;
type First = Join<["a", "b", "c"]>; // "a"
const f: First = "a";
console.log(f);

การหลีกเลี่ยงการเรียกซ้ำที่ไม่ใช่แบบหาง

การห่อการเรียกซ้ำไว้ภายในการดำเนินการชนิดข้อมูลอื่นจะทำลายตำแหน่งหางและอาจทำให้ถึงขีดจำกัดเร็วขึ้น ควรส่งต่อสถานะในตัวสะสมแทน

// Non-tail (can be costly): builds nesting around the recursive call
// type Bad<T> = T extends [infer H, ...infer R] ? [H, ...Bad<R>] : [];
// Tail-friendly alternative uses an accumulator parameter.
console.log("prefer accumulators");

การกำหนดขีดจำกัดความลึกอย่างชัดเจน

เพิ่มตัวนับความลึกเพื่อให้ชนิดข้อมูลหยุดทำงานหลังจากถึงจำนวนระดับที่กำหนด โดยแลกความครอบคลุมกับการสิ้นสุดการทำงานที่รับประกันได้

type Flatten<T, Depth extends unknown[] = []> =
  Depth["length"] extends 5
    ? T
    : T extends (infer U)[]
      ? Flatten<U, [...Depth, unknown]>
      : T;
type X = Flatten<number[][]>; // number
const x: X = 7;
console.log(x);

โดยทั่วไปความลึกจริงไม่เป็นปัญหา

ชนิดข้อมูลแบบเรียกซ้ำที่ใช้ในชีวิตประจำวัน เช่น รายการเชื่อมโยง ต้นไม้ และเจสัน แทบไม่เคยถึงขีดจำกัด เพราะข้อมูลที่คุณสร้างอินสแตนซ์นั้นมีความตื้น ขีดจำกัดนี้ส่งผลหลักต่อการคำนวณระดับชนิดข้อมูลที่ซับซ้อน

type Json = string | number | boolean | null | Json[] | { [k: string]: Json };
const v: Json = { a: { b: { c: 1 } } }; // fine, shallow
console.log(v);

การสังเกตข้อความข้อผิดพลาด

เมื่อพบข้อความว่า "ลึกเกินไปและอาจไม่สิ้นสุด" ให้ตรวจหากรณีฐานที่หายไป ตัวสะสมที่อยู่ในตำแหน่งซึ่งไม่ใช่หาง หรือข้อมูลขาเข้าที่มีขนาดใหญ่เกินไป

// Fixes: add/tighten the base case, switch to tail recursion,
// reduce input size, or cap with a depth counter.
console.log("check base case and tail position");

การเรียกซ้ำขณะทำงานเป็นคนละเรื่องกัน

ขีดจำกัดเหล่านี้เกี่ยวข้องกับการเรียกซ้ำในระดับชนิดข้อมูล ฟังก์ชันแบบเรียกซ้ำทั่วไปที่ทำงานกับชนิดข้อมูลแบบเรียกซ้ำจะทำงานขณะรันไทม์ และมีขีดจำกัดเฉพาะจากสแตกการเรียกใช้

type TreeNode<T> = { value: T; children: TreeNode<T>[] };
function count<T>(n: TreeNode<T>): number {
  return 1 + n.children.reduce((a, c) => a + count(c), 0);
}
console.log(count({ value: 1, children: [{ value: 2, children: [] }] }));

การออกแบบภายใต้ขีดจำกัด

ควรเลือกใช้ข้อมูลที่มีความตื้น รูปแบบการเรียกซ้ำแบบหางร่วมกับตัวสะสม และการจำกัดความลึกอย่างชัดเจน ด้วยแนวทางเหล่านี้ คุณแทบไม่พบขีดจำกัดดังกล่าวในโค้ดจริง

type Length<T extends unknown[]> = T["length"];
type N = Length<[1, 2, 3]>; // 3
const n: N = 3;
console.log(n);

ตรวจสอบความเข้าใจอย่างรวดเร็ว: ขีดจำกัดการเรียกซ้ำ

ทดสอบความเข้าใจเกี่ยวกับความลึกและขีดจำกัดของการเรียกซ้ำ

สรุป: ความลึกและขีดจำกัดของการเรียกซ้ำ

คุณเรียนรู้ว่า TypeScript จำกัดความลึกของการเรียกซ้ำในระดับชนิดข้อมูล รูปแบบการเรียกซ้ำแบบหางที่ใช้ตัวสะสมและการจำกัดความลึกอย่างชัดเจนช่วยได้ และข้อมูลแบบเรียกซ้ำที่ใช้ทั่วไปแทบไม่ถึงขีดจำกัด

type Reverse<T extends unknown[], Acc extends unknown[] = []> =
  T extends [infer H, ...infer R] ? Reverse<R, [H, ...Acc]> : Acc;
const r: Reverse<[1, 2]> = [2, 1];
console.log(r);

คำถามที่พบบ่อย

บทเรียน “ความลึกและขีดจำกัดของการเรียกซ้ำ” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “ความลึกและขีดจำกัดของการเรียกซ้ำ” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส TypeScript Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส TypeScript Academy มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “ความลึกและขีดจำกัดของการเรียกซ้ำ”

ทำความเข้าใจข้อจำกัดด้านความลึกของการเรียกซ้ำใน TypeScript คุณปฏิบัติ TypeScript Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน TypeScript Academy หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน TypeScript Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน

บทเรียน “ความลึกและขีดจำกัดของการเรียกซ้ำ” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน TypeScript Academy นี้ได้ไหม

ได้ บทเรียน TypeScript Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. การกำหนดชนิดข้อมูลแบบเรียกซ้ำ
  2. การกำหนดชนิดข้อมูลให้โครงสร้างต้นไม้
  3. ชนิดข้อมูลค่า JSON
  4. ความลึกและขีดจำกัดของการเรียกซ้ำ
← กลับไปที่ TypeScript Academy