TypeScript 7’s new Go-based compiler, tsgo, กำลังทำให้เครื่องมือพัฒนาที่ได้รับความนิยมอย่าง ESLint, ts-jest และ ts-morph ใช้งานไม่ได้แล้ว ปัญหาการใช้งานไม่ได้นี้จะยังคงดำเนินต่อไปจนกว่า programmatic API ของคอมไพเลอร์จะมีความเสถียรในการเปิดตัวเวอร์ชัน 7.1 ที่กำลังจะมาถึง ซึ่งหมายความว่าทีมที่ต้องพึ่งพาเครื่องมือเหล่านี้ควรระงับแผนการอัปเกรดไว้ก่อน

มีอะไรเปลี่ยนไปใน TypeScript 7

การเปิดตัวครั้งนี้ได้นำเสนอ tsgo ซึ่งเป็นการพอร์ตตัวตรวจสอบประเภท (type-checker) มาเป็นภาษา Go โดยทีม TypeScript ใช้รหัสลับว่า Project Corsa ด้วยการย้ายแกนหลักจาก JavaScript มาเป็น Go ทำให้คอมไพเลอร์สามารถประมวลผล build ได้เร็วขึ้นถึงสิบเท่า ซึ่งเป็นจุดเด่นที่ดึงดูดผู้ใช้งานกลุ่มแรกที่ต้องการลดเวลาใน CI pipelines ลงได้หลายนาที

ทำไมเครื่องมือต่างๆ ถึงทำงานผิดพลาด

เครื่องมือที่ทำงานร่วมกับ TypeScript ไม่ได้สื่อสารกับ type-checker โดยตรง แต่จะเรียกใช้ชุด internal API ที่เปิดเผยข้อมูลประเภท (type information), การวินิจฉัย (diagnostics) และการท่องโครงสร้าง AST (AST traversal) ซึ่ง API เหล่านี้ถูกเขียนขึ้นใหม่สำหรับ tsgo และยังคงมีการเปลี่ยนแปลงอยู่จนกว่าจะถึงเวอร์ชัน 7.1 ส่งผลให้เกิดความล้มเหลวต่อเนื่อง ทั้งแบบที่โปรแกรมหยุดทำงาน (crash) และแบบที่ทำงานผิดพลาดโดยไม่แจ้งเตือน (silent failure):

  • typescript-eslint – npm ปฏิเสธที่จะติดตั้งร่วมกับ TypeScript 7 หากฝืนติดตั้งจะทำให้ ESLint แสดงข้อผิดพลาด TypeError
  • ts-jest – พยายามเรียกใช้ internal methods ที่ไม่มีอยู่ในเวอร์ชัน Go แล้ว ส่งผลให้การแปลงไฟล์ทดสอบ (test-file transformation) หยุดทำงาน
  • ts-morph – คาดหวังว่าจะใช้ stable API ในการไล่ดูโครงสร้างโค้ด แต่ด้วย API ปัจจุบัน มันอาจคืนค่าผลลัพธ์ที่ผิดพลาดหรือล้มเหลวโดยไม่มีการแจ้งเตือน
  • Monorepos – tsgo ตัด generic type parameters บางตัวออกไป นำไปสู่ข้อผิดพลาดด้านประเภท (type errors) ที่จะปรากฏเฉพาะในโปรเจกต์ขนาดใหญ่ที่มีหลายแพ็กเกจเท่านั้น

เวิร์กโฟลว์ใดก็ตามที่มีการใช้ linting, การทดสอบด้วย Jest หรือการวิเคราะห์โค้ดร่วมกับ TypeScript 7 มีแนวโน้มที่จะพบกับ build ล้มเหลว (red builds)

ใครบ้างที่ได้รับผลกระทบ

  • ทีม Front-end ที่รัน ESLint เป็นส่วนหนึ่งของทุก pull request
  • บริการ Back-end ที่พึ่งพา ts-jest สำหรับการทำ unit testing
  • ไลบรารีที่ใช้ ts-morph สำหรับการสร้างโค้ด (code generation) หรือการทำเอกสาร (documentation)
  • องค์กรที่มีการตั้งค่าแบบ monorepo ซึ่งการอนุมานประเภท (type inference) มีความซับซ้อนอยู่แล้ว

หาก CI pipeline ของคุณกลายเป็นสีแดง (ล้มเหลว) หลังจากอัปเกรด TypeScript สาเหตุน่าจะเป็นหนึ่งในข้อข้างต้น

แนวทางการย้ายเวอร์ชันอย่างปลอดภัยจนกว่าจะถึง 7.1

วิธีที่ง่ายที่สุดในการรักษาความเร็วที่เพิ่มขึ้นโดยไม่ทำให้เครื่องมือพัง คือการแยกขั้นตอนการตรวจสอบประเภทที่รวดเร็วออกจากขั้นตอนการ build จริง:

  1. ตรึงเวอร์ชันหลักของ TypeScript ไว้ที่ 6.x – เพื่อรักษา stable API ที่เครื่องมือทั้งหมดคาดหวังไว้
  2. เพิ่ม @typescript/native-preview เป็น dev dependency – แพ็กเกจนี้จะมาพร้อมกับไฟล์ binary ของ tsgo เพื่อใช้ตรวจสอบประเภทอย่างรวดเร็วใน CI
  3. รัน tsgo ด้วย --noEmit เพื่อการตรวจสอบที่รวดเร็ว – ซึ่งจะตรวจสอบความถูกต้องของประเภทแต่จะไม่สร้างไฟล์ output ออกมา
  4. ใช้คอมไพเลอร์ tsc แบบดั้งเดิมสำหรับการ build จริง – เนื่องจาก tsc ยังคงสร้าง JavaScript และรองรับ API ของเวอร์ชัน 6.x
npm install -D typescript@^6.9
npm install -D @typescript/native-preview

อัปเดตสคริปต์ใน package.json ของคุณ:

{
  "scripts": {
    "typecheck:fast": "tsgo --noEmit",
    "build": "tsc"
  }
}

ด้วยการตั้งค่าแบบคู่ขนานนี้ คุณจะยังคงได้รับความเร็วที่เพิ่มขึ้นถึงสิบเท่าใน CI ในขณะที่ยังรักษาความเข้ากันได้กับ ESLint, ts-jest และ ts-morph ไว้ได้

เมื่อไหร่ที่คุณสามารถอัปเกรดเป็น 7.0 ได้ทันที

หากโค้ดของคุณมีการเรียกใช้เพียงแค่ tsc เท่านั้น โดยไม่มีการทำ linting, ไม่มี Jest หรือ ts-morph API ที่ไม่เสถียรก็จะไม่ส่งผลกระทบต่อคุณ ในกรณีเฉพาะเช่นนี้ คุณสามารถย้ายไปใช้ TypeScript 7 ได้ทันทีและเพลิดเพลินกับประสิทธิภาพที่เพิ่มขึ้นโดยไม่ต้องมีขั้นตอนเพิ่มเติม

สิ่งที่ควรเฝ้าระวัง

  • เวอร์ชัน 7.1 – ทีม TypeScript ได้ส่งสัญญาณว่า programmatic API จะถูกกำหนดให้คงที่ (frozen) ในการเปิดตัวครั้งนี้ เมื่อเวอร์ชันนี้มาถึง ตัวเชื่อมระหว่าง tsgo และเครื่องมือที่มีอยู่เดิมจะหายไป ทำให้สามารถอัปเกรดได้อย่างราบรื่น
  • การอัปเดตเครื่องมือต่างๆ – คอยติดตามการเปิดตัวเวอร์ชันใหม่ของ typescript-eslint, ts-jest และ ts-morph ซึ่งพวกเขาจะปล่อยเวอร์ชันที่รองรับออกมาในเวลาไม่นานหลังจาก 7.1 เปิดตัว
  • การตั้งค่า CI – อย่าลืมเปลี่ยนจาก tsgo --noEmit กลับมาเป็นการเรียกใช้ tsc ปกติเมื่อ API มีความเสถียรแล้ว และจะไม่จำเป็นต้องใช้แพ็กเกจ preview อีกต่อไป

สรุป: จนกว่า API ในเวอร์ชัน 7.1 จะถูกกำหนดให้คงที่ ให้ใช้ TypeScript 6.x เป็นคอมไพเลอร์หลักของคุณ, เพิ่ม @typescript/native-preview เพื่อการตรวจสอบที่รวดเร็ว และใช้ tsc ต่อไปสำหรับการ build วิธีนี้จะช่วยหลีกเลี่ยงปัญหา linting และการทดสอบที่พัง ในขณะที่ยังได้รับประโยชน์ด้านประสิทธิภาพจากเอนจินภาษา Go ตัวใหม่