ข้ามไปยังเนื้อหา

Performance & Pitfalls

การ type check คือการคำนวณจริง และ type ที่ฉลาดเกินไปทำให้ tsc คลานหรือ editor หน่วงได้ type system (ที่ขึ้นชื่อว่า) ทรงพลังพอที่จะเป็นภาษาโปรแกรมของตัวเอง — และเหมือน runtime code คือเขียนแบบที่ evaluate แพงได้เหมือนกัน

ตัวการที่พบบ่อย:

  • union ขนาดใหญ่ union ของ string literal เป็นพัน ๆ ตัว (มักถูก generate) ทวีคูณงานทุกที่ที่ถูกใช้
  • recursion ลึก recursive conditional/mapped type ที่เดิน tuple หรือ string ยาว ๆ ชน exponential blow-up และ instantiation-depth limit
  • distributive conditional บน union ใหญ่ conditional type distribute บนทุก member — บน union ใหญ่ก็คือ instantiation จำนวนมาก
  • inference ที่ทำงานหนักเกินไป API ที่ generic มากบังคับให้ checker แก้หา type argument หลายตัวในทุก call

คุณมักไม่สังเกตจนกว่าไฟล์หนึ่งใช้เวลา check เป็นวินาที หรือ red squiggle ใน editor ตามการพิมพ์ไม่ทัน นั่นคือสัญญาณให้มองที่ type ไม่ใช่แค่ code

นอกจาก performance เหล่านี้กัดทีมจริง:

  • type ฉลาดเกินจนไม่มีใครอ่านออก conditional type 40 บรรทัดที่ประหยัด duplicate ไปสามบรรทัด มักขาดทุนสุทธิ ความฉลาดคือ cost ที่ผู้อ่านในอนาคตทุกคนต้องจ่าย
  • any leak any ตัวเดียวลาม: any.foo เป็น any จึงปิดการ check เงียบ ๆ ทุกที่ที่แตะ แบนด้วย noImplicitAny และ lint rule
  • cast ที่โกหก x as Foo บอก compiler ให้เลิก check — ถ้า x ไม่ใช่ Foo จริง คุณเพิ่งซ่อน bug ไว้ double cast (x as unknown as Foo) คือธงแดงขนาดยักษ์
  • เรื่องเซอร์ไพรส์ของ enum numeric enum ยอมให้ number อะไรก็ได้เป็น value และ emit runtime code; const object กับ as const หรือ string-literal union มักชัดกว่าและถูกกว่า
  • ใช้ as เกินจำเป็น การคว้า as มา silence error มักหมายความว่าคุณเลิก model ปัญหาแล้วเริ่มเถียงกับ compiler
flowchart LR
  src["one any
(bad cast / JSON.parse)"] --> a1["any.user"]
  a1 --> a2["any.user.name"]
  a2 --> a3["passed to typed fn
checks silently skipped"]
any ตัวเดียว leak ทั่ว codebase อย่างไร

เมื่อการ check รู้สึกช้า ให้วัดแทนการเดา:

  • tsc --extendedDiagnostics พิมพ์ว่าเวลาและ memory ไปไหน (check time, instantiation count, memory)
  • tsc --generateTrace traceDir สร้าง trace ที่เปิดใน profiler เพื่อหา type ที่แพงได้
  • bisect: comment type ที่สงสัยออกแล้วดูตัวเลขขยับ

การแก้เกือบทุกครั้งคือ simplify: แทน conditional type ที่วีรบุรุษด้วย union ธรรมดา, แตก type ยักษ์เป็นชิ้นเล็กที่มีชื่อ หรือยอมรับ type ที่หลวมกว่านิดหน่อยแต่ถูกกว่า 10 เท่าและปลอดภัยพอ ๆ กันในทางปฏิบัติ

อะไรที่มักทำให้ type checker ช้า?
ทำไม `any` ตัวเดียวถึงอันตราย?
`x as Foo` ทำอะไรจริง ๆ?
เมื่อ type ทั้งฉลาดและช้า move ที่ถูกต้องมักคืออะไร?