semiเซมิโคลอน และกับดัก ASI ที่อยู่เบื้องหลังค่าเริ่มต้น

ตัวเลือก Semi ของ Prettier

semi กำหนดว่า Prettier จะปิดท้ายคำสั่งด้วยเซมิโคลอนหรือไม่ ค่าเริ่มต้นคือ true และเหตุผลไม่ได้อยู่ที่ความสวยงาม แต่เป็นเพราะการแทรกเซมิโคลอนอัตโนมัติของ JavaScript มีกรณีที่ผิดพลาดแบบคาดไม่ถึงอยู่ไม่กี่กรณี และการพิมพ์เซมิโคลอนออกมาก็ตัดกรณีเหล่านั้นออกไปจากการพิจารณาทั้งหมด

Print semicolons.
คำอธิบายของ Prettier เอง จากเอกสารตัวเลือกอย่างเป็นทางการ
ค่าเริ่มต้น
true
ชนิด
boolean
แฟล็ก CLI
--no-semi

ออปชันนี้ทำอะไร

เมื่อ semi: true ซึ่งเป็นค่าเริ่มต้น Prettier จะปิดท้ายทุกคำสั่งที่ใส่เซมิโคลอนได้ ส่วน semi: false จะละไว้ ยกเว้นในจุดที่การละจะเปลี่ยนความหมายของโปรแกรม

ข้อยกเว้นนั้นคือหัวใจของเรื่องทั้งหมด จริง ๆ แล้ว JavaScript ไม่ได้บังคับให้ใช้เซมิโคลอน เพราะตัวแยกวิเคราะห์จะแทรกให้เองตามกฎที่เรียกว่า automatic semicolon insertion หรือ ASI ซึ่งทำงานโดยสังเกตว่าโทเคนถัดไปต่อจากคำสั่งปัจจุบันไม่ได้ แล้วจึงปิดคำสั่งนั้น กฎนี้ถูกเกือบตลอด และผิดในไม่กี่กรณีที่เจ็บหนัก

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

const value = compute()
[1, 2, 3].forEach(run)

// ถูกอ่านเป็น:
const value = compute()[1, 2, 3].forEach(run)
ถ้าไม่มีเซมิโคลอน บรรทัดที่สองจะถูกอ่านเป็นการเข้าถึงดัชนีของ value

Prettier ทำอะไรเมื่อคุณปิดเซมิโคลอน

semi: false ใน Prettier ปลอดภัยในแบบที่โค้ดไร้เซมิโคลอนซึ่งเขียนด้วยมือไม่ปลอดภัย เมื่อบรรทัดใดจะขึ้นต้นด้วยอักขระที่ต่อเนื่องจากคำสั่งก่อนหน้า Prettier จะเติมเซมิโคลอนป้องกันไว้ข้างหน้า

const value = compute()
;[1, 2, 3].forEach(run)
เซมิโคลอนนำหน้านี้ Prettier เป็นคนใส่ ไม่ใช่คุณ

อักขระที่กระตุ้นพฤติกรรมนี้ได้แก่ [ ( แบ็กทิก + - / และอีกสองสามตัวที่พบไม่บ่อย คุณจะเห็นเซมิโคลอนนำหน้าแบบนี้ในโค้ดเบสไร้เซมิโคลอนทุกแห่งที่ใช้ Prettier มันดูแปลกในตอนแรกและมันไม่ใช่ของที่ตัดทิ้งได้ การลบออกด้วยมือคือการนำบั๊กที่สไตล์นี้ตั้งใจจะเลี่ยงกลับเข้ามาพอดี

ทำไมค่าเริ่มต้นถึงเป็น true

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

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

เมื่อไรควรเลือก false

เหตุผลของ semi: false มีอยู่จริง เซมิโคลอนไม่ได้ให้ข้อมูลอะไรที่ตัวจัดรูปแบบหาเองไม่ได้ และโค้ดเบสที่ไม่มีมันก็แน่นน้อยกว่าบนหน้าจออย่างวัดได้ ระบบนิเวศใหญ่หลายแห่ง ทั้งสไตล์ standard และโลกของ Vue กับ Nuxt ส่วนมาก ต่างเลือกที่จะละมัน โปรเจกต์ที่อยู่ในวงโคจรเดียวกันจึงจะรู้สึกเข้าที่กว่าเมื่อทำตามเพื่อนบ้าน

  • เลือก false ถ้าทีมของคุณเขียนแบบนี้อยู่แล้ว หรือชุมชนของเฟรมเวิร์กที่ใช้เป็นแบบนั้น ความสอดคล้องกับโค้ดที่ผู้ร่วมพัฒนาอ่านจากที่อื่นมีค่ามากกว่าความปลอดภัยส่วนเพิ่ม
  • เลือก true ถ้าทีมมีประสบการณ์คละกัน หรือโค้ดเบสอยู่ยาวและคุณไม่อยากต้องอธิบายเรื่องเซมิโคลอนนำหน้าในการรีวิว
  • อย่าเลือกจากความยากในการพิมพ์ Prettier เป็นคนเขียนมันให้ คุณไม่เคยต้องพิมพ์เองสักตัว

ESLint TypeScript และ CI

ESLint มีทั้งกฎ semi และกฎ no-unexpected-multiline และกฎแรกจะขัดกับ Prettier ถ้าเปิดทั้งคู่ ให้ปิดกฎเชิงรูปแบบด้วย eslint-config-prettier โดยวางไว้ท้ายสุดในคอนฟิก แล้วปล่อยให้ Prettier เป็นเจ้าของการตัดสินใจนี้

no-unexpected-multiline เป็นตัวที่น่าสนใจและควรเก็บไว้ถ้ามันรอดจากคอนฟิกของคุณ เพราะมันจับกับดัก ASI ข้างต้นได้ตั้งแต่ตอน lint แทนที่จะไปเจอตอนรันจริง มันไม่ใช่กฎเชิงรูปแบบจึงไม่ขัดกัน

TypeScript ไม่ได้รับผลกระทบจากการตั้งค่านี้ เซมิโคลอนเป็นทางเลือกในนั้นด้วยเหตุผลเดียวกันและมีกับดักแบบเดียวกัน การประกาศพร็อพเพอร์ตี้ของคลาสเป็นจุดเดียวที่เครื่องหมายวรรคตอนที่พิมพ์ออกมาต่างกันเล็กน้อยระหว่างสองค่านี้ และ Prettier จัดการให้แล้ว

ข้อผิดพลาดที่พบบ่อย

  • ลบเซมิโคลอนนำหน้าที่ Prettier ใส่ให้ภายใต้ semi: false มันมีหน้าที่สำคัญ
  • ปล่อยให้กฎ semi ของ ESLint ทำงานควบคู่กับ Prettier ซึ่งทำให้เกิดวงวนที่แก้ไม่ได้เพราะเครื่องมือทั้งสองคอยลบล้างกัน
  • สลับค่านี้บนโค้ดเบสที่โตแล้วโดยไม่แยกคอมมิตสำหรับการจัดรูปแบบใหม่ มันแตะเกือบทุกบรรทัดในโปรเจกต์
  • เข้าใจว่า semi: false แปลว่าจะไม่มีเซมิโคลอนโผล่ที่ไหนเลย ลูป for ยังต้องมีของมัน เพราะนั่นเป็นไวยากรณ์ ไม่ใช่ตัวปิดคำสั่ง

ใช้ใน .prettierrc

ใส่ semi ลงในไฟล์ตั้งค่า Prettier ของคุณ:

{
  "semi": true
}
ลองใช้ในตัวสร้าง

ตัวอย่างจริง

โค้ดเดียวกันที่จัดรูปแบบด้วยแต่ละค่าของ semi

semi: true

const user = {
  name: "Ada",
  "user-id": 7,
  roles: ["admin", "editor"],
  active: true,
};
const greet = (name) => `Hello ${name}`;
const label = user.active
  ? "active member of the team"
  : "inactive member of the team";
export function summarize(items) {
  return items
    .filter((i) => i.active)
    .map((i) => i.name)
    .join(", ");
}

semi: false

const user = {
  name: "Ada",
  "user-id": 7,
  roles: ["admin", "editor"],
  active: true,
}
const greet = (name) => `Hello ${name}`
const label = user.active
  ? "active member of the team"
  : "inactive member of the team"
export function summarize(items) {
  return items
    .filter((i) => i.active)
    .map((i) => i.name)
    .join(", ")
}

คำถามที่พบบ่อย

semi: false อันตรายจริงไหม
ไม่ เมื่อ Prettier เป็นตัวเขียนโค้ดให้คุณ Prettier จะแทรกเซมิโคลอนนำหน้าเชิงป้องกันในทุกบรรทัดที่จะถูกอ่านผิด อันตรายอยู่ที่โค้ดไร้เซมิโคลอนที่เขียนด้วยมือ หรือการแก้ผลลัพธ์ของ Prettier เองแล้วลบเซมิโคลอนนำหน้าเหล่านั้นออก
ทำไมไฟล์ของฉันขึ้นต้นบรรทัดด้วยเซมิโคลอน
เพราะบรรทัดนั้นขึ้นต้นด้วย [ ( แบ็กทิก หรือตัวดำเนินการ และถ้าไม่มีเซมิโคลอน JavaScript จะเชื่อมมันเข้ากับคำสั่งก่อนหน้า Prettier ใส่ให้โดยตั้งใจภายใต้ semi: false
ออปชันนี้มีผลกับ TypeScript JSON หรือ CSS ไหม
มีผลกับ JavaScript และ TypeScript ส่วน JSON ไม่มีคำสั่งจึงไม่เกี่ยวข้อง และการประกาศใน CSS ปิดท้ายด้วยเซมิโคลอนเสมอตามไวยากรณ์ ออปชันนี้จึงไม่มีผลที่นั่นเช่นกัน

ตัวเลือก JavaScript อื่น ๆ

สร้างจาก Prettier 3.9.6