สำรวจประเภทลิเทอรัลเทมเพลตอันทรงพลังของ TypeScript สำหรับการจัดการสตริงขั้นสูง การจับคู่รูปแบบ และการตรวจสอบ เรียนรู้ด้วยตัวอย่างเชิงปฏิบัติและกรณีการใช้งานจริง
ประเภทลิเทอรัลเทมเพลต: การจับคู่รูปแบบสตริงและการตรวจสอบใน TypeScript
ระบบประเภทของ TypeScript มีการพัฒนาอย่างต่อเนื่อง โดยนำเสนอเครื่องมือที่ทรงพลังยิ่งขึ้นสำหรับนักพัฒนาเพื่อแสดงตรรกะที่ซับซ้อนและรับประกันความปลอดภัยของประเภท คุณสมบัติที่น่าสนใจและหลากหลายที่สุดอย่างหนึ่งที่เปิดตัวในเวอร์ชันล่าสุดคือ ประเภทลิเทอรัลเทมเพลต ประเภทเหล่านี้ช่วยให้คุณจัดการสตริงในระดับประเภท ซึ่งช่วยให้สามารถจับคู่รูปแบบสตริงและการตรวจสอบขั้นสูงได้ สิ่งนี้เปิดโลกแห่งความเป็นไปได้ใหม่ทั้งหมดสำหรับการสร้างแอปพลิเคชันที่แข็งแกร่งและบำรุงรักษาได้มากขึ้น
ประเภทลิเทอรัลเทมเพลตคืออะไร
ประเภทลิเทอรัลเทมเพลตเป็นรูปแบบของประเภทที่สร้างขึ้นโดยการรวมประเภทสตริงลิเทอรัลและประเภทยูเนียน คล้ายกับวิธีที่ลิเทอรัลเทมเพลตทำงานใน JavaScript อย่างไรก็ตาม แทนที่จะสร้างสตริงรันไทม์ พวกมันจะสร้างประเภทใหม่ตามประเภทที่มีอยู่
นี่คือตัวอย่างพื้นฐาน:
type Greeting<T extends string> = `Hello, ${T}!`;
type MyGreeting = Greeting<"World">; // type MyGreeting = "Hello, World!"
ในตัวอย่างนี้ `Greeting` เป็นประเภทลิเทอรัลเทมเพลตที่รับประเภทสตริง `T` เป็นอินพุต และส่งกลับประเภทใหม่ซึ่งเป็นการต่อกันของ "Hello, ", `T` และ "!"
การจับคู่รูปแบบสตริงพื้นฐาน
ประเภทลิเทอรัลเทมเพลตสามารถใช้เพื่อทำการจับคู่รูปแบบสตริงพื้นฐานได้ สิ่งนี้ช่วยให้คุณสร้างประเภทที่ถูกต้องก็ต่อเมื่อตรงกับรูปแบบที่แน่นอนเท่านั้น
ตัวอย่างเช่น คุณสามารถสร้างประเภทที่ยอมรับเฉพาะสตริงที่ขึ้นต้นด้วย "prefix-" ได้:
type PrefixedString<T extends string> = T extends `prefix-${string}` ? T : never;
type ValidPrefixedString = PrefixedString<"prefix-valid">; // type ValidPrefixedString = "prefix-valid"
type InvalidPrefixedString = PrefixedString<"invalid">; // type InvalidPrefixedString = never
ในตัวอย่างนี้ `PrefixedString` ใช้ประเภทตามเงื่อนไขเพื่อตรวจสอบว่าสตริงอินพุต `T` ขึ้นต้นด้วย "prefix-" หรือไม่ ถ้าเป็นเช่นนั้น ประเภทคือ `T` เอง มิฉะนั้นจะเป็น `never` `never` เป็นประเภทพิเศษใน TypeScript ที่แสดงถึงประเภทของค่าที่ไม่เคยเกิดขึ้น ซึ่งเป็นการยกเว้นสตริงที่ไม่ถูกต้องอย่างมีประสิทธิภาพ
การแยกส่วนต่างๆ ของสตริง
ประเภทลิเทอรัลเทมเพลตยังสามารถใช้เพื่อแยกส่วนต่างๆ ของสตริงได้ สิ่งนี้มีประโยชน์อย่างยิ่งเมื่อคุณต้องการแยกวิเคราะห์ข้อมูลจากสตริงและแปลงเป็นประเภทต่างๆ
สมมติว่าคุณมีสตริงที่แสดงถึงพิกัดในรูปแบบ "x:10,y:20" คุณสามารถใช้ประเภทลิเทอรัลเทมเพลตเพื่อแยกค่า x และ y ได้:
type CoordinateString = `x:${number},y:${number}`;
type ExtractX<T extends CoordinateString> = T extends `x:${infer X},y:${number}` ? X : never;
type ExtractY<T extends CoordinateString> = T extends `x:${number},y:${infer Y}` ? Y : never;
type XValue = ExtractX<"x:10,y:20">; // type XValue = 10
type YValue = ExtractY<"x:10,y:20">; // type YValue = 20
ในตัวอย่างนี้ `ExtractX` และ `ExtractY` ใช้คีย์เวิร์ด `infer` เพื่อจับส่วนต่างๆ ของสตริงที่ตรงกับประเภท `number` `infer` ช่วยให้คุณแยกประเภทจากการจับคู่รูปแบบได้ จากนั้นประเภทที่จับได้จะถูกใช้เป็นประเภทการส่งคืนของประเภทตามเงื่อนไข
การตรวจสอบสตริงขั้นสูง
ประเภทลิเทอรัลเทมเพลตสามารถใช้ร่วมกับคุณสมบัติ TypeScript อื่นๆ เช่น ประเภทยูเนียนและประเภทตามเงื่อนไข เพื่อทำการตรวจสอบสตริงขั้นสูง สิ่งนี้ช่วยให้คุณสร้างประเภทที่บังคับใช้กฎที่ซับซ้อนเกี่ยวกับโครงสร้างและเนื้อหาของสตริงได้
ตัวอย่างเช่น คุณสามารถสร้างประเภทที่ตรวจสอบสตริงวันที่ ISO 8601 ได้:
type Year = `${number}${number}${number}${number}`;
type Month = `0${number}` | `10` | `11` | `12`;
type Day = `${0}${number}` | `${1 | 2}${number}` | `30` | `31`;
type ISODate = `${Year}-${Month}-${Day}`;
type ValidDate = ISODate extends "2023-10-27" ? true : false; // true
type InvalidDate = ISODate extends "2023-13-27" ? true : false; // false
function processDate(date: ISODate) {
// Function logic here. TypeScript enforces the ISODate format.
return `Processing date: ${date}`;
}
console.log(processDate("2024-01-15")); // Works
//console.log(processDate("2024-1-15")); // TypeScript error: Argument of type '"2024-1-15"' is not assignable to parameter of type '`${number}${number}${number}${number}-${0}${number}-${0}${number}` | `${number}${number}${number}${number}-${0}${number}-${1}${number}` | ... 14 more ... | `${number}${number}${number}${number}-12-31`'.
ที่นี่ `Year`, `Month` และ `Day` ถูกกำหนดโดยใช้ประเภทลิเทอรัลเทมเพลตเพื่อแสดงรูปแบบที่ถูกต้องสำหรับแต่ละส่วนของวันที่ จากนั้น `ISODate` จะรวมประเภทเหล่านี้เพื่อสร้างประเภทที่แสดงถึงสตริงวันที่ ISO 8601 ที่ถูกต้อง ตัวอย่างนี้ยังแสดงให้เห็นว่าประเภทนี้สามารถใช้เพื่อบังคับใช้รูปแบบข้อมูลในฟังก์ชันได้อย่างไร ซึ่งป้องกันไม่ให้รูปแบบวันที่ที่ไม่ถูกต้องถูกส่งผ่าน สิ่งนี้ช่วยปรับปรุงความน่าเชื่อถือของโค้ดและป้องกันข้อผิดพลาดรันไทม์ที่เกิดจากอินพุตที่ไม่ถูกต้อง
กรณีการใช้งานจริง
ประเภทลิเทอรัลเทมเพลตสามารถใช้ได้ในสถานการณ์จริงต่างๆ นี่คือตัวอย่างบางส่วน:
- การตรวจสอบแบบฟอร์ม: คุณสามารถใช้ประเภทลิเทอรัลเทมเพลตเพื่อตรวจสอบรูปแบบของอินพุตแบบฟอร์ม เช่น ที่อยู่อีเมล หมายเลขโทรศัพท์ และรหัสไปรษณีย์
- การตรวจสอบคำขอ API: คุณสามารถใช้ประเภทลิเทอรัลเทมเพลตเพื่อตรวจสอบโครงสร้างของ payload คำขอ API เพื่อให้มั่นใจว่าเป็นไปตามรูปแบบที่คาดไว้ ตัวอย่างเช่น การตรวจสอบรหัสสกุลเงิน (เช่น "USD", "EUR", "GBP")
- การแยกวิเคราะห์ไฟล์กำหนดค่า: คุณสามารถใช้ประเภทลิเทอรัลเทมเพลตเพื่อแยกวิเคราะห์ไฟล์กำหนดค่าและแยกค่าตามรูปแบบเฉพาะ พิจารณาการตรวจสอบพาธไฟล์ในออบเจ็กต์การกำหนดค่า
- Enums ที่ใช้สตริง: คุณสามารถสร้าง enums ที่ใช้สตริงพร้อมการตรวจสอบโดยใช้ประเภทลิเทอรัลเทมเพลต
ตัวอย่าง: การตรวจสอบรหัสสกุลเงิน
มาดูตัวอย่างโดยละเอียดเพิ่มเติมเกี่ยวกับการตรวจสอบรหัสสกุลเงิน เราต้องการให้แน่ใจว่ามีการใช้เฉพาะรหัสสกุลเงิน ISO 4217 ที่ถูกต้องในแอปพลิเคชันของเรา รหัสเหล่านี้มักจะเป็นตัวอักษรตัวพิมพ์ใหญ่สามตัว
type CurrencyCode = `${Uppercase<string>}${Uppercase<string>}${Uppercase<string>}`;
function formatCurrency(amount: number, currency: CurrencyCode) {
// Function logic to format currency based on the provided code.
return `$${amount} ${currency}`;
}
console.log(formatCurrency(100, "USD")); // Works
//console.log(formatCurrency(100, "usd")); // TypeScript error: Argument of type '"usd"' is not assignable to parameter of type '`${Uppercase}${Uppercase}${Uppercase}`'.
//More precise example:
type ValidCurrencyCode = "USD" | "EUR" | "GBP" | "JPY" | "CAD" | "AUD"; // Extend as needed
type StronglyTypedCurrencyCode = ValidCurrencyCode;
function formatCurrencyStronglyTyped(amount: number, currency: StronglyTypedCurrencyCode) {
return `$${amount} ${currency}`;
}
console.log(formatCurrencyStronglyTyped(100, "EUR")); // Works
//console.log(formatCurrencyStronglyTyped(100, "CNY")); // TypeScript error: Argument of type '"CNY"' is not assignable to parameter of type '"USD" | "EUR" | "GBP" | "JPY" | "CAD" | "AUD"'.
ตัวอย่างนี้แสดงให้เห็นวิธีสร้างประเภท `CurrencyCode` ที่ยอมรับเฉพาะสตริงที่ประกอบด้วยอักขระตัวพิมพ์ใหญ่สามตัว ตัวอย่างที่สองที่พิมพ์อย่างเข้มงวดยิ่งขึ้นแสดงให้เห็นวิธีจำกัดสิ่งนี้ให้แคบลงไปอีกในรายการสกุลเงินที่ยอมรับได้ที่กำหนดไว้ล่วงหน้า
ตัวอย่าง: การตรวจสอบพาธ API Endpoint
อีกกรณีการใช้งานหนึ่งคือการตรวจสอบพาธ API endpoint คุณสามารถกำหนดประเภทที่แสดงถึงโครงสร้าง API endpoint ที่ถูกต้อง เพื่อให้มั่นใจว่ามีการส่งคำขอไปยังเส้นทางที่ถูกต้อง สิ่งนี้มีประโยชน์อย่างยิ่งในสถาปัตยกรรม microservices ที่บริการหลายรายการอาจเปิดเผย API ที่แตกต่างกัน
type APIServiceName = "users" | "products" | "orders";
type APIEndpointPath = `/${APIServiceName}/${string}`;
function callAPI(path: APIEndpointPath) {
// API call logic
console.log(`Calling API: ${path}`);
}
callAPI("/users/123"); // Valid
callAPI("/products/details"); // Valid
//callAPI("/invalid/path"); // TypeScript error
// Even more specific:
type APIAction = "create" | "read" | "update" | "delete";
type APIEndpointPathSpecific = `/${APIServiceName}/${APIAction}`;
function callAPISpecific(path: APIEndpointPathSpecific) {
// API call logic
console.log(`Calling specific API: ${path}`);
}
callAPISpecific("/users/create"); // Valid
//callAPISpecific("/users/list"); // TypeScript error
สิ่งนี้ช่วยให้คุณกำหนดโครงสร้างของ API endpoint ได้อย่างแม่นยำยิ่งขึ้น ป้องกันการพิมพ์ผิดและรับประกันความสอดคล้องในแอปพลิเคชันของคุณ นี่เป็นตัวอย่างพื้นฐาน สามารถสร้างรูปแบบที่ซับซ้อนกว่าเพื่อตรวจสอบพารามิเตอร์ query และส่วนอื่นๆ ของ URL
ประโยชน์ของการใช้ประเภทลิเทอรัลเทมเพลต
การใช้ประเภทลิเทอรัลเทมเพลตสำหรับการจับคู่รูปแบบสตริงและการตรวจสอบมีประโยชน์หลายประการ:
- ความปลอดภัยของประเภทที่ได้รับการปรับปรุง: ประเภทลิเทอรัลเทมเพลตช่วยให้คุณบังคับใช้ข้อจำกัดประเภทที่เข้มงวดมากขึ้นกับสตริง ลดความเสี่ยงของข้อผิดพลาดรันไทม์
- ความสามารถในการอ่านโค้ดที่ได้รับการปรับปรุง: ประเภทลิเทอรัลเทมเพลตทำให้โค้ดของคุณอ่านง่ายขึ้นโดยแสดงรูปแบบที่คาดหวังของสตริงอย่างชัดเจน
- ความสามารถในการบำรุงรักษาที่เพิ่มขึ้น: ประเภทลิเทอรัลเทมเพลตทำให้โค้ดของคุณบำรุงรักษาได้มากขึ้นโดยการให้แหล่งข้อมูลเดียวสำหรับกฎการตรวจสอบสตริง
- ประสบการณ์ของนักพัฒนาซอฟต์แวร์ที่ดีขึ้น: ประเภทลิเทอรัลเทมเพลตให้การเติมข้อความอัตโนมัติและข้อความแสดงข้อผิดพลาดที่ดีขึ้น ปรับปรุงประสบการณ์ของนักพัฒนาซอฟต์แวร์โดยรวม
ข้อจำกัด
แม้ว่าประเภทลิเทอรัลเทมเพลตจะมีประสิทธิภาพ แต่ก็มีข้อจำกัดบางประการ:
- ความซับซ้อน: ประเภทลิเทอรัลเทมเพลตอาจมีความซับซ้อน โดยเฉพาะอย่างยิ่งเมื่อต้องจัดการกับรูปแบบที่ซับซ้อน เป็นสิ่งสำคัญที่จะต้องสร้างสมดุลระหว่างประโยชน์ของความปลอดภัยของประเภทกับการบำรุงรักษาโค้ด
- ประสิทธิภาพ: ประเภทลิเทอรัลเทมเพลตอาจส่งผลต่อประสิทธิภาพการคอมไพล์ โดยเฉพาะอย่างยิ่งในโครงการขนาดใหญ่ นี่เป็นเพราะ TypeScript จำเป็นต้องทำการตรวจสอบประเภทที่ซับซ้อนมากขึ้น
- การสนับสนุน Regular Expression ที่จำกัด: แม้ว่าประเภทลิเทอรัลเทมเพลตจะอนุญาตให้มีการจับคู่รูปแบบ แต่ก็ไม่รองรับคุณสมบัติ regular expression ทั้งหมด สำหรับการตรวจสอบสตริงที่ซับซ้อนอย่างมาก regular expression รันไทม์อาจยังคงจำเป็นควบคู่ไปกับโครงสร้างประเภทเหล่านี้สำหรับการฆ่าเชื้ออินพุตที่เหมาะสม
แนวทางปฏิบัติที่ดีที่สุด
นี่คือแนวทางปฏิบัติที่ดีที่สุดบางประการที่ควรคำนึงถึงเมื่อใช้ประเภทลิเทอรัลเทมเพลต:
- เริ่มต้นอย่างง่าย: เริ่มต้นด้วยรูปแบบง่ายๆ และค่อยๆ เพิ่มความซับซ้อนตามความจำเป็น
- ใช้ชื่อที่สื่อความหมาย: ใช้ชื่อที่สื่อความหมายสำหรับประเภทลิเทอรัลเทมเพลตของคุณเพื่อปรับปรุงความสามารถในการอ่านโค้ด
- จัดทำเอกสารประเภทของคุณ: จัดทำเอกสารประเภทลิเทอรัลเทมเพลตของคุณเพื่ออธิบายวัตถุประสงค์และการใช้งาน
- ทดสอบอย่างละเอียด: ทดสอบประเภทลิเทอรัลเทมเพลตของคุณอย่างละเอียดเพื่อให้แน่ใจว่าทำงานได้ตามที่คาดไว้
- พิจารณาประสิทธิภาพ: ระลึกถึงผลกระทบของประเภทลิเทอรัลเทมเพลตต่อประสิทธิภาพการคอมไพล์และปรับโค้ดของคุณให้เหมาะสมตามนั้น
สรุป
ประเภทลิเทอรัลเทมเพลตเป็นคุณสมบัติที่ทรงพลังใน TypeScript ที่ช่วยให้คุณทำการจัดการสตริงขั้นสูง การจับคู่รูปแบบ และการตรวจสอบในระดับประเภท การใช้ประเภทลิเทอรัลเทมเพลต คุณสามารถสร้างแอปพลิเคชันที่แข็งแกร่ง บำรุงรักษาได้ และปลอดภัยต่อประเภทมากขึ้น แม้ว่าจะมีข้อจำกัดบางประการ แต่ประโยชน์ของการใช้ประเภทลิเทอรัลเทมเพลตมักจะมากกว่าข้อเสีย ทำให้เป็นเครื่องมือที่มีค่าในคลังแสงของนักพัฒนา TypeScript ทุกคน ในขณะที่ภาษา TypeScript ยังคงพัฒนาต่อไป การทำความเข้าใจและการใช้คุณสมบัติประเภทขั้นสูงเหล่านี้จะเป็นสิ่งสำคัญสำหรับการสร้างซอฟต์แวร์คุณภาพสูง อย่าลืมสร้างสมดุลระหว่างความซับซ้อนและความสามารถในการอ่าน และให้ความสำคัญกับการทดสอบอย่างละเอียดเสมอ