ts-pattern: ทำให้โค้ด TypeScript อ่านง่ายและเช็กเงื่อนไขได้ครบขึ้น
ts-pattern คือไลบรารีที่นำแนวคิด pattern matching มาใช้ใน TypeScript เพื่อทำให้โค้ดที่มีเงื่อนไขหลายแบบอ่านง่ายขึ้น กระชับขึ้น และช่วยเช็กได้ว่ารองรับทุกกรณีสำคัญแล้วหรือยัง

ts-pattern: ทำให้โค้ด TypeScript อ่านง่ายและเช็กเงื่อนไขได้ครบขึ้น
เคยไหม เขียน TypeScript แล้วต้องเจอกับ if/else หรือ switch ที่ซ้อนหลายชั้นจนกลับมาอ่านเองแล้วยังงงว่าโค้ดกำลังตัดสินใจอะไรอยู่บ้าง ปัญหานี้เกิดขึ้นบ่อยมาก โดยเฉพาะในงานที่มีหลายสถานะ หลายรูปแบบข้อมูล และ business logic ที่ค่อย ๆ ซับซ้อนขึ้นตามการเติบโตของโปรเจกต์
หนึ่งในเครื่องมือเล็ก ๆ ที่ช่วยให้โค้ดลักษณะนี้ชัดขึ้นมากคือ ts-pattern ไลบรารีที่พาแนวคิด pattern matching มาอยู่ในโลกของ TypeScript ทำให้การจัดการเงื่อนไขหลายรูปแบบเป็นระเบียบ อ่านง่าย และลดโอกาสพลาดบาง case ได้อย่างมีนัยสำคัญ
ts-pattern คืออะไร
ts-pattern เป็นไลบรารีสำหรับเขียน logic แบบจับคู่รูปแบบของข้อมูล หรือที่เรียกว่า pattern matching แทนที่จะค่อย ๆ เช็กเงื่อนไขทีละขั้นเหมือน if/else หรือ switch เราสามารถประกาศได้เลยว่า ถ้าข้อมูลมีหน้าตาแบบนี้ ให้ทำอะไร
แนวคิดหลักของมันคือ:
matchข้อมูลที่รับเข้ามา- ระบุ pattern ที่ต้องการจับในแต่ละกรณี
- ปิดท้ายด้วย
.exhaustive()เพื่อบังคับให้เช็กว่าครบทุกกรณีแล้ว
จุดเด่นสำคัญคือมันทำงานร่วมกับ TypeScript ได้ดีมาก โดยเฉพาะกับ union type, type narrowing และ exhaustive checking
ทำไมโค้ดแบบเดิมถึงเริ่มอ่านยาก
ตัวอย่างที่พบได้บ่อยในหลายโปรเจกต์คือการจัดการสถานะของการโหลดข้อมูล เช่น
loadingsuccesserrorempty
ในโค้ดแบบทั่วไป หลายคนมักเริ่มจาก switch(status) แล้วภายในบาง case ยังมีการแตกเงื่อนไขต่ออีกที เมื่อเวลาผ่านไป โค้ดจะยาวขึ้น มีทางเลือกมากขึ้น และความเสี่ยงที่จะลืมรองรับบางกรณีก็เพิ่มขึ้นตาม
ปัญหาที่ตามมาคือ:
- อ่าน flow การตัดสินใจยาก
- refactor แล้วไม่มั่นใจว่าครบทุกกรณี
- เมื่อทีมเพิ่ม state ใหม่ อาจเกิด bug ตอน runtime โดยไม่รู้ตัว
จุดแข็งของ ts-pattern ที่หลายคนชอบ
เหตุผลที่ ts-pattern ได้รับความนิยมคือมันไม่ได้ช่วยแค่ให้โค้ด “สวย” ขึ้น แต่ช่วยให้โค้ด “ปลอดภัย” ขึ้นด้วย
ข้อดีที่เห็นชัดมีดังนี้:
- โค้ดกระชับและอ่านง่ายขึ้น
- ลดการตกหล่นของเงื่อนไข
- refactor ได้มั่นใจขึ้น
- ใช้กับ TypeScript union type ได้ดีมาก
- ทำให้ intent ของโค้ดชัดเจนตั้งแต่แรก
โดยเฉพาะ .exhaustive() ถือเป็นคุณสมบัติที่มีประโยชน์มาก เพราะเมื่อมีการเพิ่ม state ใหม่ใน type ระบบจะเริ่มเตือนทันทีหากยังมีจุดที่ match ไม่ครบ แทนที่จะปล่อยให้ bug ไปเกิดตอนใช้งานจริง
จับคู่ตาม shape ของข้อมูลได้ตรงกว่าเดิม
สิ่งที่น่าสนใจคือ ts-pattern ไม่ได้จำกัดอยู่แค่การเช็กค่าธรรมดา แต่สามารถจับคู่ได้ตามโครงสร้างข้อมูล เช่น object, array, nested structure และเงื่อนไขที่ซับซ้อนกว่าเดิม
ตัวอย่างรูปแบบข้อมูลที่พบได้บ่อย:
{ status: 'success', data: [...] }
{ status: 'error', error: 'timeout' }
{ status: 'loading' }
แทนที่จะเขียนโค้ดไล่เช็กทีละ field เราสามารถอธิบายได้ตรง ๆ ว่า หากข้อมูลมีโครงสร้างแบบใด ให้ไปทำงานเส้นทางนั้นเลย
ภาพจำง่าย ๆ คือ:
if/elseเหมือนเดินเลือกทางทีละแยก- pattern matching เหมือนมองแผนผังทั้งหมดก่อน แล้วระบุเส้นทางตามรูปแบบของข้อมูลทันที
แนวคิดนี้ทำให้โค้ดคล้ายการ “อธิบายโครงสร้างข้อมูล” มากกว่าการ “ไล่เช็กเงื่อนไข” ซึ่งอ่านง่ายมากเมื่อกลับมาดูภายหลัง
ตัวอย่างงานที่เหมาะกับ ts-pattern
ts-pattern เหมาะมากกับงานที่มี state หรือรูปแบบข้อมูลหลายชนิด เช่น
- render UI ตาม state ของ API
- จัดการ event หลายประเภท
- แยก logic ตาม role ของ user
- parse payload จาก webhook
- ตรวจ action ของ reducer ใน state management
- ระบบ dashboard, workflow, payment, permission และ event processing
ตัวอย่างแนวคิดแบบใกล้ตัว:
match(payment)
- เจอ
{ status: 'paid' }→ อนุมัติ - เจอ
{ status: 'pending' }→ รอตรวจสอบ - เจอ
{ status: 'failed' }→ แจ้งเตือน - ถ้าเขียนไม่ครบ → TypeScript เตือน
เมื่อ business logic มีหลายแขนง วิธีคิดแบบนี้จะช่วยให้แต่ละกรณีถูกระบุไว้อย่างชัดเจน และลดความคลุมเครือในโค้ดลงได้มาก
ช่วยลด bug เมื่อทีมขยายฟีเจอร์
อีกมุมหนึ่งที่สำคัญมากคือ ts-pattern ช่วยทีมรับมือกับการเปลี่ยนแปลงของระบบในระยะยาวได้ดี
ลองนึกภาพว่า วันนี้ระบบมี status แค่ success กับ error แต่เดือนหน้าทีมเพิ่ม archived เข้ามา หากยังใช้โค้ดที่ไม่มีการเช็กครบทุกกรณี บางจุดอาจลืมรองรับสถานะใหม่นี้ และ bug จะไปโผล่ตอน runtime
แต่ถ้าใช้ ts-pattern พร้อม exhaustive checking จุดที่ยังไม่รองรับ archived จะถูกเตือนทันทีตั้งแต่ตอนพัฒนา ทำให้ทีมแก้ไขได้เร็วและมั่นใจขึ้นมาก
นี่จึงเป็นเหตุผลที่หลายทีมเลือกใช้มันกับระบบที่มี business logic หนาแน่น เพราะการเปลี่ยนแปลงเล็ก ๆ ใน type สามารถส่งสัญญาณเตือนไปยังจุดที่ได้รับผลกระทบได้ทันที
เรื่อง performance ควรมองอย่างไร
ในแง่ performance โดยทั่วไป ts-pattern เหมาะกับงานแอปพลิเคชันทั่วไปได้สบาย จุดเด่นของมันไม่ใช่การรีดความเร็วแบบสุดขีด แต่คือการทำให้โค้ดชัดขึ้น ดูแลง่ายขึ้น และปลอดภัยขึ้นในระยะยาว
ดังนั้นถ้าบริบทของงานคือการพัฒนาแอปที่ต้องแก้ไขต่อเนื่อง มีหลาย state และมีทีมทำงานร่วมกัน ประโยชน์ด้านความอ่านง่ายและความมั่นใจในการ refactor มักคุ้มค่ามาก
วิธีเริ่มใช้แบบง่ายที่สุด
ถ้าอยากเริ่มต้นโดยไม่ต้องเปลี่ยนแนวทางทั้งโปรเจกต์ วิธีที่ง่ายที่สุดคือเริ่มจากจุดที่มี switch ยาว ๆ ก่อน โดยเฉพาะโค้ดที่ทำงานกับ union type ชัดเจน
แนวทางแนะนำ:
- หา
switchหรือif/elseที่มีหลายกรณี - เลือกจุดที่มี state ชัดเจน เช่น
loading | success | error | empty - แทนที่ logic นั้นด้วยการ
match - ปิดท้ายด้วย
.exhaustive()เพื่อให้ TypeScript ช่วยเช็กให้ครบ - เมื่อเริ่มคุ้นแล้ว ค่อยขยับไปใช้กับ object pattern ที่ซับซ้อนขึ้น
สำหรับมือใหม่ ts-pattern อาจดูเป็นแนวคิดใหม่ในช่วงแรก แต่ถ้าคุ้นกับ object, union type และการแยก state อยู่แล้ว จะเรียนรู้ได้ค่อนข้างเร็ว
ส่วนสำหรับคนที่มีประสบการณ์ มันเป็นเครื่องมือที่ช่วยให้ TypeScript แสดงศักยภาพได้เต็มขึ้น โดยเฉพาะในเรื่อง type narrowing และ exhaustive checking
สรุป
ts-pattern เป็นตัวอย่างของไลบรารีเล็ก ๆ ที่สร้างผลลัพธ์ใหญ่ในงานประจำวันได้จริง มันช่วยให้โค้ดที่เต็มไปด้วยเงื่อนไขหลายแขนงกลายเป็นโค้ดที่ชัด กระชับ และปลอดภัยขึ้น โดยเฉพาะเมื่อทำงานกับ union type และ state ที่ซับซ้อน
หากคุณกำลังเจอกับ if/else ซ้อนหลายชั้น หรือ switch ที่ยาวจนเริ่มไม่มั่นใจว่าครบทุกกรณีหรือยัง ts-pattern คือเครื่องมือที่ควรลองอย่างยิ่ง เพราะบางครั้งสิ่งที่ช่วยทีมได้มากที่สุด ไม่ใช่ framework ขนาดใหญ่ แต่เป็นไลบรารีเล็ก ๆ ที่ทำให้โค้ดดีขึ้นทีละนิดในทุกวัน