Haliviq
TypeScript

satisfies เก็บ literal type ไว้ และตรวจ shape ไปพร้อมกัน

ถ้าเราระบุ type ให้ตัวแปรตรง ๆ type จะกว้างขึ้นและเสีย literal value จริงไป ส่วน satisfies ตรวจ object ตาม type แต่ยังเก็บ inference แบบแคบไว้ เหมาะกับ config หรือ route map ที่อยากได้ทั้ง autocomplete ของ key จริงและการตรวจ shape ให้ถูกต้อง

เกิดอะไรขึ้น

เรามี route map ที่เขียนเป็น object แล้วใส่ type ให้ตัวแปรเป็น Record<string, Route> ตรวจ shape ได้ก็จริง แต่ autocomplete ไม่เห็น key จริงอย่าง home หรือ login และพิมพ์ key ผิดก็ไม่ error เพราะ type บอกว่า string ไหนก็ได้

เราอยากได้ทั้งสองอย่าง คือให้ compiler ตรวจว่าทุก entry มี shape ถูก และให้ editor รู้ว่า key ไหนมีอยู่จริง

ทำไมถึงเป็นแบบนั้น

เมื่อเราเขียน type annotation ตัวแปรจะมี type ตามที่ระบุ ไม่ใช่ตามค่าที่ใส่ จึงเหลือแต่ข้อมูลกว้าง ๆ ของ Record<string, Route> ส่วนข้อมูลเฉพาะของ object ที่เขียนไว้หายไป

satisfies (มาตั้งแต่ TypeScript 4.9) ตรวจ expression เทียบกับ type แล้ว ไม่เปลี่ยน type ของตัวแปร type ที่ได้ยังมาจากค่าที่เขียนจริง key จึงเป็นชุดที่แน่นอน และ property ที่ type เป้าหมายกำหนดเป็น literal union เช่น 'GET' | 'POST' ก็คงเป็น literal เดิม ถ้าอยากให้ทุกค่าเป็น literal ให้ใช้ as const ร่วมด้วย

ตอนนี้เราทำอย่างไร

ตอนนี้ config และ route map ของเราใช้ satisfies แทน annotation พิมพ์ key ผิดแล้ว compiler เตือนทันที autocomplete เห็นเฉพาะ key จริง และถ้ามี entry ไหนผิด shape ก็ error ที่ entry นั้นเลย

ถ้าต้องการให้ตัวแปรกว้างโดยตั้งใจ เช่นรับค่าได้หลายแบบ ก็ยังใช้ annotation ตามปกติได้ satisfies เหมาะกับ object ที่เรารู้ว่ามีอะไรอยู่ข้างใน

ตรวจ shape แต่ยังรู้ key จริงts
type Route = { path: string; method: 'GET' | 'POST' }

const routes = {
  home: { path: '/', method: 'GET' },
  login: { path: '/login', method: 'POST' },
} satisfies Record<string, Route>

routes.login.method // 'POST'
routes.logn // error: Property 'logn' does not exist

เช็กลิสต์สั้น ๆ

  1. 1ลบ type annotation ออกจากตัวแปร
  2. 2ใส่ satisfies ตามด้วย type ที่ต้องการตรวจท้าย object
  3. 3ลองพิมพ์ key ผิดเพื่อดูว่า compiler เตือน
  4. 4ถ้าต้องการ literal ทั้งหมด เพิ่ม as const ก่อน satisfies

จำไว้สั้น ๆ

ใช้ satisfies เมื่ออยากให้ compiler ตรวจ shape โดยไม่ทำให้ type ของ object กว้างขึ้น

เราอยากฟังว่าคุณกำลังสร้างอะไรอยู่

คุยกับเรา