ค้นหาเว็บไซต์

angular - Typescript (จริง ๆ) ปฏิบัติตามหลักเกณฑ์การตั้งชื่อสำหรับประเภทที่มีพารามิเตอร์ (T, U, V, W) ใน Generics หรือไม่?


ฉันไม่แน่ใจเลยว่าใน TS เราปฏิบัติตามหลักการตั้งชื่อประเภทที่มีพารามิเตอร์แบบนี้เหมือนกับ C++/Java หรือหลายภาษาอื่น ๆ (T,U,V,W) หรือไม่

ฉันเห็นหลายครั้งเกี่ยวกับการใช้งานแบบผสมของแนวปฏิบัติประเภทที่มีพารามิเตอร์ใน TS ตัวอย่างเช่น ในบันทึกการอัปเดตของ TS 2.8:

type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any;

ทำไมถึงเป็น R ไม่ใช่ U?

จากนั้น ในตัวอย่างถัดไป:

type Unpacked<T> = T extends (infer U)[]
  ? U
  : T extends (...args: any[]) => infer U
  ? U
  : T extends Promise<infer U>
  ? U
  : T;

ทำไมถึงเป็น U ไม่ใช่ R?

ตัวอย่างอีกตัวอย่างหนึ่ง ในบล็อกโค้ดเดียวกันของโค้ดที่อยู่ด้านบน:

type T0 = Unpacked<string>; // string
type T1 = Unpacked<string[]>; // string
type T2 = Unpacked<() => string>; // string
type T3 = Unpacked<Promise<string>>; // string
type T4 = Unpacked<Promise<string>[]>; // Promise<string>
type T5 = Unpacked<Unpacked<Promise<string>[]>>; // string

แต่ในซอร์สโค้ด TS:

// We represent tuple types as type references to synthesized generic interface types created by
// this function. The types are of the form:
//
//   interface Tuple<T0, T1, T2, ...> extends Array<T0 | T1 | T2 | ...> { 0: T0, 1: T1, 2: T2, ... }

คำถามด้านบนเป็นเพียงตัวอย่างเพื่อชี้ให้เห็น คุณไม่จำเป็นต้องตอบพวกมัน ฉันสามารถเพิ่มตัวอย่างอื่นที่ U ถูกปฏิบัติในบางครั้งเหมือนชนิดการคืนค่า และบางครั้งก็แค่เป็นชนิดที่สอง

คำถามที่แท้จริงของฉันคือ: เรากำลังปฏิบัติตามมาตรฐานของ Java Generic Types สำหรับ TS Generics จริง ๆ หรือไม่? ถ้าไม่ TS ปฏิบัติตามมาตรฐานประเภทใดสำหรับ Generics? มีมาตรฐานประเภทที่แตกต่างกันกี่แบบที่อยู่ร่วมกันใน TS (นอกเหนือจากที่ชัดเจนคือ PascalCase สำหรับชื่อ Type และ camelCase สำหรับคุณสมบัติ/วิธีของ type)?

คำตอบ #1

(ส่วนใหญ่จากความคิดเห็น)

สิ่งที่ใกล้เคียงที่สุดที่ผมสามารถจินตนาการได้ในการตอบเรื่องนี้ในลักษณะที่ไม่ใช่เพียงความคิดเห็นของผมคือการอ้างถึงเอกสารบางอย่างที่อธิบายว่า "แนวทางการตั้งชื่อพารามิเตอร์ชนิดใน TypeScript" คืออะไร และเปรียบเทียบกับเอกสารสอน Java Generics ที่คุณลิงก์มา

ถ้าเป็นเช่นนั้น ท่าทีอย่างเป็นทางการของทีมออกแบบ TypeScript ต่อเรื่องนี้ดูเหมือนจะเป็น "เราไม่ได้ตั้งใจจะบังคับใช้ข้อกำหนดใด ๆ เหล่านี้กับผู้อื่น" หรือ "ไม่มีข้อกำหนดการตั้งชื่อที่เป็นมาตรฐานใน TS" ดูได้ที่ microsoft/TypeScript#6168 และ microsoft/TypeScript#878 โดยเฉพาะความคิดเห็นนี้:

โดยทั่วไปแล้ว เราไม่ได้สนใจที่จะตัดสินเรื่องสไตล์ให้กับผู้คน การกล่าวว่ามีสไตล์ที่ได้รับการยอมรับเพียงหนึ่งเดียวสำหรับ TS นั้นขัดแย้งกับปรัชญาของเราที่เรามีอยู่เพื่อให้ชนิดสำหรับ JS ไม่ว่าคุณจะเขียนมันอย่างไร (แน่นอน ภายในขอบเขตที่เหมาะสม 😉)

นอกจากนี้ยังมีกฎ naming-convention ของ ESLint และกฎ naming-convention ของ TSLint ซึ่งสามารถทำให้เป็นไปได้ในการบังคับใช้ข้อตกลงการตั้งชื่อพารามิเตอร์ประเภทในฐานรหัสที่ตรวจสอบด้วยลินเตอร์ แต่ดูเหมือนไม่ทำโดยค่าเริ่มต้น ดังนั้นไม่มีข้อตกลงใดที่ดูเหมือนจะเป็นทางการพอที่จะถูกบังคับใช้โดยค่าเริ่มต้น


เพื่อการเปรียบเทียบ ลองมาดูส่วนที่เกี่ยวข้องจากเอกสารสอน Java Generics ที่คุณลิงก์มา:

หลักเกณฑ์การตั้งชื่อพารามิเตอร์ชนิด

ตามธรรมเนียม ชื่อพารามิเตอร์ประเภทมักเป็นตัวอักษรตัวเดียวและเป็นตัวพิมพ์ใหญ่ ซึ่งตรงข้ามอย่างชัดเจนกับแนวปฏิบัติการตั้งชื่อตัวแปรที่คุณรู้จักอยู่แล้ว และด้วยเหตุผลที่ดี: หากไม่มีธรรมเนียมนี้ จะเป็นเรื่องยากที่จะบอกความแตกต่างระหว่างตัวแปรประเภทกับชื่อคลาสหรืออินเทอร์เฟซปกติ

ชื่อพารามิเตอร์ประเภทที่ใช้กันมากที่สุดคือ:

  • E - อิลิเมนต์ (ใช้กันอย่างแพร่หลายโดย Java Collections Framework)
  • K - กุญแจ
  • N - ตัวเลข
  • T - ประเภท
  • V - ค่า
  • S, U, V เป็นต้น - ประเภทที่ 2, 3, 4

คุณจะเห็นชื่่อเหล่านี้ถูกใช้ตลอด API ของ Java SE และส่วนที่เหลือของบทเรียนนี้

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

ส่วนที่เกี่ยวกับการเลือก "ตัวอักษรเดี่ยว ตัวพิมพ์ใหญ่" นั้น ใกล้เคียง กับคำแนะนำ: ตัวอักษรพิมพ์ใหญ่มักจะแยกชื่อ ชนิด ออกจากชื่อ ตัวแปร และชื่อชนิดที่เป็นตัวอักษรตัวเดียวมักจะแยก พารามิเตอร์ชนิด ออกจาก ชนิดเฉพาะ อย่างเช่นคลาสหรืออินเทอร์เฟซ แต่ฉันก็ได้รับความรู้สึกเดียวกันว่านี่ไม่ใช่คำสั่งจากเบื้องบนมากนัก แต่เป็นการสังเกตเกี่ยวกับแนวปฏิบัติทั่วไป


ดังนั้นเราจึงอาจหยุดเพียงแค่นั้นและกล่าวว่า "ไม่มีแนวทางการตั้งชื่อพารามิเตอร์ประเภทอย่างเป็นทางการหรือเป็นมาตรฐานในทั้ง TypeScript หรือ Java และแนวทางที่ไม่เป็นทางการนั้นขึ้นอยู่กับความคิดเห็น"

แต่เพื่อความพยายามในการสรุปสิ่งที่ผม คิดว่า ประเพณีที่ไม่เป็นทางการใน TypeScript จริงๆ เป็น อย่างไร ผมจะพูดต่อ โปรดจำไว้ว่า นี่เป็นความคิดเห็นของผม และคนอื่นอาจไม่เห็นด้วยได้


ฉันคงจะบอกว่าข้อกำหนดการตั้งชื่อของ Java ที่ได้กล่าวมาข้างต้นสอดคล้องอย่างใกล้ชิดกับสิ่งที่ฉันพิจารณาว่าเป็นข้อกำหนดการตั้งชื่อที่ใช้กันทั่วไปสำหรับพารามิเตอร์ชนิดเจเนอริกของ TypeScript: ใช้ตัวอักษรตัวใหญ่เพียงตัวเดียว ซึ่งสอดคล้องกับตัวอักษรแรกของสิ่งที่มันแทน เช่น:

  • T หมายถึง "type" ซึ่งเป็นชื่อพารามิเตอร์ประเภทที่ทั่วไปที่สุดและดังนั้นจึงเป็นชื่อที่ใช้กันมากที่สุด
  • K สำหรับ "key" หรือ P สำหรับ "property" ทั้งคู่มีแนวโน้มที่จะถูกจำกัดโดย PropertyKey หรือ keyof T หรือ keyof SomeInterface หรือ keyof SomeClass;
  • V สำหรับ "ค่า" ซึ่งมักใช้เป็นคู่กับ K สำหรับ "กุญแจ"
  • A สำหรับ "arguments" และ R สำหรับ "return" ซึ่งสอดคล้องกับรายการพารามิเตอร์ที่เหลือและประเภทการคืนค่าของลายเซ็นฟังก์ชันตามลำดับ เช่น (...args: A) => R;
  • N สำหรับ "number", S สำหรับ "string", B สำหรับ "boolean", สำหรับพารามิเตอร์ประเภทที่ถูกจำกัดโดยประเภทพื้นฐาน;

หรือบางลำดับของประเภทที่เกี่ยวข้อง เช่น:

  • T, U, V, W เป็นต้น เริ่มต้นด้วย T สำหรับ "type" แล้วจึงไล่ตามตัวอักษรเมื่อจำเป็นต้องใช้ชนิดเพิ่ม โดยต้องจำไว้ว่าคุณสามารถได้เพียงไม่กี่ชนิดด้วยวิธีนี้
  • A, B, C, D ฯลฯ เริ่มจากตัวอักษรแรกของตัวอักษรภาษาอังกฤษเมื่อคุณคาดว่าจะใช้พารามิเตอร์ชนิดจำนวนมากและคุณยังไม่ได้ใช้พารามิเตอร์ชนิดสำหรับสิ่งอื่น

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

  • T0, T1, T2, T3 เป็นต้น การต่อท้ายตัวเลขเพื่อให้ได้ลำดับของประเภทที่เกี่ยวข้อง;
  • KT, KU, KV: การเติมหน้า K สำหรับ "คีย์ของ" T, U และ V ตามลำดับ;

มันอยู่ห่างจากสิ่งที่ฉันถือว่าธรรมดา แต่ก็ยังพอพบได้บ่อยพอที่จะเขียนชื่อสั้นๆ แบบ UpperCamelCase เพื่อให้สื่อความหมายของประเภทที่แทนค่าได้ชัดเจนขึ้น โดยมีข้อเสียคือมันอาจเริ่มสับสนกับประเภทเฉพาะเจาะจงและไม่ใช่พารามิเตอร์ประเภท:

  • คีย์, ค่า, คุณสมบัติ, อาร์กิวเมนต์, ผลลัพธ์, ประเภท, นี้

สิ่งต่อไปนี้เป็นสิ่งที่ไม่ธรรมดา (จำไว้ว่า นี่คือความคิดเห็นของฉัน!) และควรหลีกเลี่ยงเว้นแต่จะมีเหตุผลพิเศษที่จำเป็นมากในการทำเช่นนั้น:

  • ชื่อยาวที่ดูเหมือนชื่ออินเทอร์เฟซหรือคลาส เช่น InputType หรือ Properties;
  • การใส่ตัวอักษรใหญ่ T ไว้หน้าชื่อประเภทที่ยาวขึ้น เช่น TNotRecommended;
  • ชื่อที่เริ่มต้นด้วยตัวอักษรเล็ก เช่น t หรือ u หรือ myType;

บทความที่เกี่ยวข้อง