All posts

11 min readTypeScript

ย้าย Next.js ไป TypeScript 7: วัดอะไรก่อนอัป และให้ AI agent ช่วยคุ้มไหม

ก่อนสั่ง agent ย้าย Next.js ไป TypeScript 7 ลองวัดก่อนว่าจะได้อะไรคืน blog ตัวนี้ยังอยู่ TS 5.7.2 และ tsc ใช้เวลาแค่ราว 3 วินาที บันทึกจากเดฟ·PM·PO เรื่องเส้นทางผ่าน TS6, จุดที่ tooling มักติด, สิ่งที่ agent ทำแทนได้ และเกณฑ์ที่คนต้องเขียนเองก่อนเริ่ม

Sutthiphong NuanmaSoftware Developer

คำสั่งอัป TypeScript 7 สั้นมากครับ สั้นจนน่าส่งให้ agent ไปเลยในประโยคเดียวว่า "ช่วยอัปโปรเจกต์นี้ไป TS7 ให้หน่อย"

แล้วเราก็น่าจะได้ PR ที่ tsc ผ่าน พร้อมสรุปที่อ่านแล้วสบายใจ แต่ถ้าไม่มีใครวัดเวลาไว้ก่อน เราจะตอบไม่ได้ว่าการอัปครั้งนั้นได้อะไรกลับมา และถ้าไม่มีใครเขียนไว้ว่าเสร็จแปลว่าอะไร tsc ผ่านก็จะกลายเป็นคำว่าเสร็จไปเอง

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

บอกไว้ก่อนว่าผมยังไม่ได้อัป blog ตัวนี้ ในโพสต์จึงไม่มีตัวเลขหลังอัป มีแค่ baseline ของจริงกับวิธีวัดครับ

จุดยืนของผมมีอยู่ว่า การอัปเกรดที่ไม่มีตัวเลขก่อนเริ่มและไม่มีเกณฑ์ว่าเสร็จ จะดูสำเร็จเสมอ ไม่ว่าคนหรือ agent จะเป็นคนทำ

โจทย์: อยากได้ความเร็ว แต่ 7.0 ไม่มี API

TypeScript 7.0 ออกเมื่อ 8 ก.ค. 2026 เป็น compiler ที่พอร์ตเป็น Go Microsoft บอกว่า full build มักเร็วขึ้นราว 8–12 เท่า ย้ำว่านี่เป็นตัวเลขของผู้ผลิตบน codebase ที่เขาเลือกวัด ไม่ใช่ตัวเลขของโปรเจกต์นี้ครับ

เงื่อนไขสำคัญคือ 7.0 ไม่มี programmatic API เครื่องมือที่ import แพ็กเกจ typescript มาเรียก compiler จึงยังต้องพึ่ง 6.0 ผ่าน @typescript/typescript6 ซึ่งให้คำสั่ง tsc6 และ re-export API เดิม รายละเอียดกับการตั้งค่า package.json อยู่ในโพสต์ก่อนหน้า แล้วครับ

ส่วน 7.1 Beta เมื่อ 20 ต.ค. 2026 เริ่มมี JS API ใหม่ แต่ยังเป็น Beta และเครื่องมือต้องพอร์ตมาใช้ก่อน ตอนนี้จึงยังไม่ควรวางแผนโดยนับว่าปัญหานี้หมดไปแล้ว

ตัวเลขจริงของ blog นี้: baseline แค่ไม่กี่วินาที

โปรเจกต์นี้ใช้ Next.js 16.0.10 ส่วน package.json ระบุ typescript เป็น ^5 และ version ที่ติดตั้งจริงคือ 5.7.2

ตัวเลขแรกที่ผมจดจาก npx tsc --noEmit คือราว 3.1 วินาที แต่ตอนนั้นไม่ได้บันทึกเงื่อนไขไว้ พอวัดซ้ำบนเครื่องเดียวกันในวันเดียวกัน (8 คอร์) วิธีรันละ 5 รอบ ได้แบบนี้ครับ

วิธีรันช่วงเวลาค่ากลาง
npx tsc --noEmit มี tsconfig.tsbuildinfo อยู่แล้ว1.7–1.9 วินาที1.8 วินาที
npx tsc --noEmit --incremental false2.7–3.0 วินาที2.8 วินาที

เวลาพวกนี้รวม overhead ของ npx ด้วย และยังเป็นตัวเลขของ TS 5.7.2 เท่านั้น ไม่มีตัวเลขของ TS6 หรือ TS7 ปนอยู่ครับ

จากตารางนี้ผมได้เรื่องที่ต้องระวังสองข้อ

ข้อแรก คำสั่งเดียวกันให้เวลาต่างกันได้เยอะ tsconfig ของโปรเจกต์ Next.js ตัวนี้เปิด incremental: true ถ้าเผลอเอารอบที่ไม่มี cache ของรุ่นเก่าไปเทียบกับรอบที่มี cache ของรุ่นใหม่ เราจะได้ส่วนต่างที่ไม่เกี่ยวกับ compiler เลย

ข้อสอง baseline บอกเพดานของผลตอบแทน ต่อให้ tsc ใช้เวลาเป็นศูนย์ blog นี้ก็ประหยัดได้ไม่เกินราว 3 วินาทีต่อรอบ นี่เป็นเลขคณิตธรรมดา ไม่ใช่การทำนายผล TS7 ครับ ถ้ารันไม่กี่ครั้งต่อวัน เหตุผลด้านความเร็วก็แทบไม่มี จะอัปก็ควรเป็นเพราะเหตุผลอื่น เช่น ไม่อยากปล่อยให้ตามหลังนานเกินไป

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

วัดอะไรบ้าง ก่อนและหลังอัป

ผมจะวัดสี่อย่างนี้ โดยใช้ชุดเดียวกันทุกขั้น ทั้งบน 5.x, 6 และ 7

1. tsc --noEmit แยกแบบมีและไม่มี cache

time npx tsc --noEmit --incremental false
time npx tsc --noEmit

รันอย่างละหลายรอบแล้วใช้ค่ากลาง และจดเวอร์ชัน เครื่อง กับจำนวนคอร์ไว้ด้วยครับ ตารางของ Microsoft วัดที่ค่า default --checkers 4 เครื่อง local กับ CI runner ที่คอร์ไม่เท่ากันจึงอาจได้ผลไม่เท่ากัน

2. เวลา typecheck ใน CI ไม่ใช่แค่บนเครื่องเรา

ดูเวลาของขั้น typecheck แยกจากขั้นอื่น แล้วเทียบกับเวลาทั้ง pipeline ถ้าขั้นนี้กินสัดส่วนน้อย ถึงเร็วขึ้นมากก็อาจแทบไม่เห็นผลในภาพรวม ส่วน repo นี้ไม่มี workflow ใน .github/workflows ขั้นนี้จึงยังไม่มีตัวเลขให้เทียบ

3. next build ทั้งเส้น

blog นี้ build ด้วย ANALYZE=true next build --webpack ซึ่งทำมากกว่า typecheck และผมยังไม่ได้วัดเวลาไว้ในโพสต์นี้ครับ ตอนเทียบต้องคุมเรื่อง cache ของ .next ให้เหมือนกัน และดูด้วยว่าขั้น type check ของ Next ยังทำงานอยู่จริง ไม่ใช่แค่ build ผ่าน

4. ESLint ยังวิ่งได้ไหม และใช้เวลาเท่าไร

ข้อนี้เป็นคำถามผ่าน/ไม่ผ่านก่อนเรื่องเวลา ให้รัน npm run lint แล้วดูว่ายังตรวจไฟล์ครบ ไม่ใช่แค่จบโดยไม่มี error และอย่าคาดว่า lint จะเร็วตาม tsc โดยอัตโนมัติ โพสต์ก่อนหน้า ยกคำของ maintainer typescript-eslint ไว้ว่ารุ่นแรกที่รองรับ TS7 ไม่ได้คาดว่าจะเร็วกว่า TS6 มาก

เส้นทางย้าย: ทีละขั้น และหยุดได้ทุกขั้น

ทีม TypeScript แนะนำให้ขึ้น 6.0 ก่อน เพราะ 6 เป็นสะพาน ของที่ deprecate ใน 6 จะเป็น error ใน 7 การขึ้น 6 จึงเป็นการเห็นรายการหนี้ทั้งหมดในรุ่นที่ยังเปิดทางให้ค่อย ๆ แก้ได้

ขั้นที่ 1 ขึ้น 6 และเก็บ deprecation ใช้ ignoreDeprecations พักชั่วคราวได้ แต่ถ้ายังต้องพึ่งมันอยู่ก็ยังไม่จบขั้นนี้ ประกาศของ 7.0 ระบุว่าโค้ดที่ compile ผ่านบน 6.0 โดยเปิด stableTypeOrdering และไม่ตั้ง ignoreDeprecations ควร compile ได้เหมือนกันบน 7.0 แทบทั้งหมด ข้อนี้ใช้เป็นเกณฑ์จบขั้นได้เลยครับ

ขั้นที่ 2 ลอง 7 ถ้าไม่มีอะไรต้องใช้ API เดิม ลง 7 เดี่ยว ๆ ได้ ถ้ายังมี ก็ใช้ typescript เป็น alias ของ 6 และวาง 7 ไว้ใต้ @typescript/native คำสั่ง tsc จะเป็น 7 ส่วน tsc6 จะเป็น 6

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

แต่ละขั้นมีประโยชน์ในตัวเองครับ ถ้าวัดแล้วไม่คุ้มไปต่อ ก็หยุดที่ 6 ได้โดยไม่เสียงานที่ทำมา

อะไรมักติด

ระดับทั่วไปมีสองกลุ่มหลักตามประกาศ คือ typescript-eslint ที่ต้องเข้าถึง compiler แบบ programmatic และเครื่องมือของ Vue, Svelte, Astro, MDX ที่ฝัง TypeScript ไว้ใน compiler และ language service ของตัวเอง โปรเจกต์ Next.js ที่มี .mdx หรือใช้เครื่องมือกลุ่มนี้ จึงควรตรวจแยกทีละตัว

ฝั่ง Next.js เองขึ้นกับเวอร์ชันครับ docs ฉบับปัจจุบัน บอกว่า next build ใช้ tsc ของโปรเจกต์เป็น default แต่ในเวอร์ชันเก่ากว่าอาจไม่เป็นแบบนั้น

ผมลองไล่ใน node_modules ของ repo นี้ ซึ่งยังไม่ได้อัปอะไร และเจอสามจุดที่ควรรู้ก่อนเริ่ม

  • npm ls typescript แสดงว่า eslint-config-next ดึง typescript-eslint 8.48.1 เข้ามา แม้ package.json จะไม่มีชื่อมันโดยตรง
  • typescript-eslint รุ่นนี้ประกาศ peer dependency เป็น typescript >=4.8.4 <6.0.0 ดังนั้นแค่ขั้นแรกที่ขึ้น 6 ก็ออกนอกช่วงที่รุ่นนี้ประกาศไว้แล้ว และต้องเช็กว่ามีรุ่นใหม่กว่าที่รองรับหรือเปล่า
  • runTypeCheck.js ของ Next 16.0.10 เรียก createProgram และ createIncrementalProgram ตรง ๆ และไม่พบ useTypeScriptCli ใน next/dist ขั้น type check ของรุ่นนี้จึงยังพึ่ง API เดิม

ทั้งหมดนี้มาจากการอ่านโค้ดและ metadata ครับ ผมยังไม่ได้ลองอัป จึงยังไม่รู้ว่าจะพังจริงแค่ไหนหรือแบบไหน บอกได้แค่ว่าสามจุดนี้ต้องอยู่ในแผน โดยเฉพาะข้อที่สอง ซึ่งถ้าดูแค่ประกาศของ TS7 จะคิดว่าปัญหาเริ่มที่ 7 ทั้งที่ของเราเริ่มตั้งแต่ 6

ให้ agent ช่วยย้ายคุ้มไหม

คำตอบสั้น ๆ ของผมคือคุ้มครับ ถ้าส่วนที่เป็นกลไกแยกออกจากส่วนที่เป็นการตัดสินใจ

งานย้ายรุ่นเป็นงานที่ agent อย่าง Claude หรือ Codex ถนัด เพราะมี error เป็นตัวบอกว่าเหลืออะไร งานกลุ่มนี้มอบหมายได้เลย

  • แก้ package.json และ flag ใน tsconfig ตามรายการ deprecation
  • ไล่แก้ error ทีละก้อน เช่น baseUrl หรือ asserts ที่ต้องเปลี่ยนเป็น with
  • หาว่าอะไรใน repo พึ่ง typescript บ้าง และเช็ก peer dependency
  • รันคำสั่งวัดตามที่กำหนดซ้ำ ๆ แล้วทำตารางเทียบ

แต่มีสองอย่างที่ agent สร้างเองไม่ได้ คือเราอัปไปทำไม และคำว่าเสร็จของทีมเราแปลว่าอะไร

ถ้าไม่มีสองข้อนี้ agent ก็ต้องเดาเอาเอง และคำเดาที่ฟังขึ้นที่สุดก็มักจะเป็น "tsc ผ่าน" นี่คือหนี้เจตนา แบบที่เล็กมากแต่เห็นชัด ส่วนถ้าเราได้ package.json ที่ alias สลับชื่อกันโดยไม่มีใครในทีมอธิบายได้ว่าทำไม typescript ถึงเป็น 6 อันนั้นก็เริ่มเป็นหนี้ความเข้าใจ ครับ

จุดที่ผมจะตรวจเองใน diff มีตามนี้ ไม่ได้แปลว่า agent ทำแบบนี้ทุกครั้ง แต่ทุกข้อทำให้ error หายได้โดยที่ปัญหายังอยู่

  • ignoreDeprecations ที่โผล่มาแล้วไม่หายไปตอนจบงาน
  • // @ts-ignore หรือ @ts-expect-error ที่เพิ่มขึ้น
  • typescript.ignoreBuildErrors ใน next.config ซึ่งทำให้ build ผ่านโดยไม่ได้ตรวจ type
  • ค่าใหม่ใน types, rootDir หรือ paths ที่ไม่มีเหตุผลกำกับ
  • ไฟล์ที่หลุดจากขอบเขต lint ทำให้ lint ผ่านเพราะตรวจน้อยลง

เรื่องตัวเลขก็เหมือนกัน ถ้าให้ agent วัด ผมจะกำหนดคำสั่งให้ และขอ output ดิบมาด้วย ไม่รับแค่ตารางสรุป เพราะสิ่งที่ต้องตรวจคือวิธีวัดเท่า ๆ กับผลที่ออกมา

ตัวอย่าง brief สมมติที่คนเขียนส่วนเหตุผลและเกณฑ์เอง แล้วให้ agent ลงมือครับ เลขในนี้เป็นแค่ตัวอย่าง แต่ละทีมต้องตั้งเอง

เป้าหมาย: ลดเวลารอ typecheck ใน CI ไม่ได้อัปเพื่อใช้ฟีเจอร์ใหม่

ขอบเขตรอบนี้: ขึ้น TypeScript 6 เท่านั้น ยังไม่ขึ้น 7
เสร็จเมื่อ:
- tsc --noEmit ผ่านโดยไม่ตั้ง ignoreDeprecations
- npm run lint และ next build ผ่าน โดยขอบเขตการตรวจไม่ลดลง
- ไม่เพิ่ม ts-ignore, ts-expect-error หรือ ignoreBuildErrors

ถ้าแก้ไม่ได้โดยไม่ละเมิดข้อข้างบน ให้หยุดแล้วรายงาน
ห้ามปรับเกณฑ์เอง

วัด: คำสั่งตาม docs/ts-upgrade-baseline.md แนบ output ดิบใน PR

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

Checklist สำหรับทีม

  1. เขียนเหตุผลและเกณฑ์ก่อน อัปเพราะอะไร ได้ตัวเลขแบบไหนถึงจะไปต่อ และแบบไหนจะหยุด
  2. วัด baseline tsc --noEmit แบบมีและไม่มี cache, typecheck ใน CI, next build และ ESLint
  3. ไล่ dependency รัน npm ls typescript แล้วเช็ก peer range ของ typescript-eslint และเวอร์ชัน Next.js ที่ล็อกอยู่จริง
  4. ขึ้น 6 อัปเครื่องมือที่ติด peer range ไปพร้อมกัน และทำเป็น PR แยก
  5. เก็บ deprecation จนไม่ต้องพึ่ง ignoreDeprecations รัน stableTypeOrdering เพื่อตรวจความต่างระหว่าง 6 กับ 7 แล้วปิด เพราะทีม TS เตือนว่าอาจทำให้ type check ช้าลงได้ถึง 25%
  6. ทดลอง 7 ใช้ @typescript/native คู่กับ tsc6 ถ้าเครื่องมือยังต้องการ API เดิม
  7. เทียบตัวเลข ใช้คำสั่ง เครื่อง และเงื่อนไข cache เดียวกับ baseline
  8. ตัดสินใจจากเกณฑ์ข้อแรก ถ้าไม่ถึงเกณฑ์ก็ย้อนหรือหยุดที่ 6 ได้ และบันทึกเหตุผลไว้ใน repo พร้อมเงื่อนไขถอด tsc6

ข้อ 1 กับข้อ 8 ควรเป็นงานของคนครับ ข้อที่เหลือให้ agent ช่วยได้เกือบหมด แต่คนต้องอ่านผล

ท้ายที่สุด

ถ้าถามว่า blog ตัวนี้ควรรีบย้ายไหม ด้วย baseline ราว 3 วินาที คำตอบของผมตอนนี้คือไม่ต้องรีบเรื่องความเร็วครับ งานที่คุ้มกว่าคือเตรียมทางขึ้น 6 และเช็ก peer range ของ typescript-eslint ก่อน แต่ถ้าโปรเจกต์ของคุณรอ typecheck เป็นนาทีทุกวัน คำตอบอาจกลับด้านเลย และนั่นคือเหตุผลที่ต้องวัดของตัวเอง

ตัวเลข 8–12 เท่าบอกว่า compiler ทำอะไรได้ แต่ไม่ได้บอกว่าทีมเราควรทำอะไร

ดังนั้นก่อนส่งคำว่า "ช่วยอัปไป TS7 ให้หน่อย" ให้ agent ผมจะถามตัวเองแค่ว่า ถ้า PR นี้กลับมาเขียวทุกช่อง ผมจะรู้ไหมว่ามันคุ้มหรือเปล่า

ถ้ารู้ ก็ส่งงานได้เลยครับ ถ้าไม่รู้ ให้เริ่มจาก time npx tsc --noEmit ก่อน