Generics တွေရဲ့ အသုံးဝင်ပုံ - Functions, Interfaces, Classes တွေမှာ သုံးခြင်း
Generic Functions
Section titled “Generic Functions”Generics တွေက အရှေ့မှာ ပြောခဲ့တဲ့ identity Function လိုမျိုး ရိုးရှင်းတာတွေ တင်မကပါဘူး။ တခြား ရှုပ်ထွေးတဲ့ Function အမျိုးမျိုးမှာလည်း သုံးလို့ရပါတယ်။ ဥပမာအနေနဲ့ Data တစ်ခုခုကို လက်ခံပြီး အဲ့ဒီ Data တစ်ခုတည်း ပါဝင်တဲ့ Array အသစ် တစ်ခု ပြန်ထုတ်ပေးမယ့် Function လေးတစ်ခု ရေးကြည့်ရအောင်။
// Generic Type Variable <T> ကို သုံးမယ်function wrapInArray<T>(input: T): T[] { return [input];}
// wrapInArray Function ကို သုံးကြည့်မယ်let numberArray = wrapInArray(10); // 10 ဆိုတဲ့ number ကို ထည့်လိုက်တော့ T က number လို့ မှန်းသိတယ်၊ ပြန်ထွက်တဲ့ Type က number[] ဖြစ်သွားတယ်console.log(numberArray); // Output: [10]
let stringArray = wrapInArray("World"); // "World" ဆိုတဲ့ string ကို ထည့်လိုက်တော့ T က string ဖြစ်သွားတယ်၊ ပြန်ထွက်တဲ့ Type က string[] ဖြစ်သွားတယ်console.log(stringArray); // Output: ["World"]
let userObject = { id: 1, name: "Admin" };let userArray = wrapInArray(userObject); // { id: number; name: string; } ဆိုတဲ့ Object ထည့်လိုက်တော့ T က အဲ့ဒီ Object Type ဖြစ်သွားတယ်၊ ပြန်ထွက်တဲ့ Type က အဲ့ဒီ Object Type ရဲ့ array[] ဖြစ်သွားတယ်console.log(userArray); // Output: [{ id: 1, name: "Admin" }]Generic Interfaces
Section titled “Generic Interfaces”Interfaces တွေက Object တွေရဲ့ ပုံစံ (Shape) ကို သတ်မှတ်ပေးတယ်ဆိုတာ သိထားပြီးသားနော်။ ဒီ Interface တွေရဲ့ ပုံစံကိုလည်း Generics သုံးပြီး ပိုပြီး လိုက်လျောညီထွေ ဖြစ်အောင် ဖန်တီးလို့ရပါတယ်။ API ကနေ ပြန်လာတဲ့ Response တွေအတွက် Standard ပုံစံတစ်ခု သတ်မှတ်တာက Generic Interface ရဲ့ အကောင်းဆုံး လက်တွေ့ ဥပမာတစ်ခုပါပဲ။
// ApiResponse ဆိုတဲ့ Interface ကို Generic ဖြစ်အောင် လုပ်မယ်// <DataType> ဆိုတာ ကျွန်တော်တို့ သုံးမယ့် Generic Type Variable ပေါ့interface ApiResponse<DataType> { data: DataType; // API ကနေ ပြန်လာမယ့် တကယ့် Data ရဲ့ Type က ဒီ DataType ပဲ ဖြစ်ရမယ် success: boolean; // အောင်မြင်လား မအောင်မြင်ဘူးလား timestamp: Date; // ဘယ်အချိန်လဲ error?: string; // Error ရှိရင် စာသားပြမယ် (ပါချင်မှ ပါမယ်)}
// ဒီ Generic Interface ကို ဘယ်လို သုံးမလဲ ကြည့်ရအောင်interface User { id: number; username: string; } // User Object ရဲ့ ပုံစံinterface Product { sku: string; price: number; name: string; } // Product Object ရဲ့ ပုံစံ
// API ကနေ User တစ်ယောက်ရဲ့ Data ပြန်လာမယ်ဆိုရင် ApiResponse<User> လို့ သုံးမယ်let userResponse: ApiResponse<User> = { data: { id: 1, username: "ts_fan" }, // data ရဲ့ Type က User အတိအကျ ဖြစ်ရမယ် success: true, timestamp: new Date()};
// API ကနေ Product တွေရဲ့ Array ပြန်လာမယ်ဆိုရင် ApiResponse<Product[]> လို့ သုံးမယ်let productsResponse: ApiResponse<Product[]> = { data: [ // data ရဲ့ Type က Product Array အတိအကျ ဖြစ်ရမယ် { sku: "TSHIRT01", price: 25.99, name: "TypeScript Logo Tee" }, { sku: "MUG004", price: 12.50, name: "Generic Mug" } ], success: true, timestamp: new Date()};
// ဒီလို သုံးလိုက်တော့ TypeScript က data ထဲမှာ ဘာ Type ရှိလဲဆိုတာ သေချာ သိနေပြီconsole.log(userResponse.data.username); // data က User ဖြစ်လို့ .username ကို စိတ်ချလက်ချ သုံးလို့ရတယ်console.log(productsResponse.data[0].price); // data က Product[] ဖြစ်လို့ data[0] က Product တစ်ခု ဖြစ်မယ်၊ သူ့ရဲ့ .price ကို သုံးလို့ရတယ်Generic Classes
Section titled “Generic Classes”Classes တွေမှာလည်း Generics ကို သုံးလို့ ရပါတယ်။ ဥပမာအားဖြင့် Data တွေကို အစီအစဉ်အတိုင်း ထည့်သွင်းပြီး၊ နောက်ဆုံးမှ ထည့်တဲ့ Data ကို အရင်ဆုံး ပြန်ထုတ်ယူရတဲ့ Data Structure တစ်ခုဖြစ်တဲ့ Stack (Last-In, First-Out) ရိုးရှင်းတဲ့ ပုံစံတစ်ခုကို Class နဲ့ တည်ဆောက်ကြည့်ရအောင်。 Classes တွေမှာလည်း Generics ကို သုံးလို့ ရပါတယ်။ ဥပမာအားဖြင့် Data တွေကို အစီအစဉ်အတိုင်း ထည့်သွင်းပြီး, နောက်ဆုံးမှ ထည့်တဲ့ Data ကို အရင်ဆုံး ပြန်ထုတ်ယူရတဲ့ Data Structure တစ်ခုဖြစ်တဲ့ Stack (Last-In, First-Out) ရိုးရှင်းတဲ့ ပုံစံတစ်ခုကို Class နဲ့ တည်ဆောက်ကြည့်ရအောင်။
// Stack Class ကို Generic ဖြစ်အောင် <ElementType> သုံးလိုက်မယ်// ဒီ Stack ထဲမှာ ဘယ်လို Type မျိုးနဲ့ Data တွေ ထည့်မလဲဆိုတာ ElementType က သတ်မှတ်ပေးမယ်class Stack<ElementType> { // ဒီ Stack ထဲက data တွေကို သိမ်းဆည်းပေးမယ့် array private items: ElementType[] = [];
// Stack ထဲကို Data ထည့်မယ့် Function push(item: ElementType): void { // ထည့်မယ့် item ရဲ့ Type ဟာ Stack ရဲ့ ElementType အတိုင်း ဖြစ်ရမယ် this.items.push(item); }
// Stack ထဲက နောက်ဆုံး Data ကို ပြန်ထုတ်မယ့် Function pop(): ElementType | undefined { // ပြန်ထွက်လာမယ့် Data ရဲ့ Type က Stack ရဲ့ ElementType ပဲ ဖြစ်မယ်၊ ဒါမှမဟုတ် ဘာမှ မရှိရင် undefined ပေါ့ return this.items.pop(); }
// Stack ထဲက နောက်ဆုံး Data ကို ကြည့်မယ့် Function (အပြင်ကို ထုတ်ယူခြင်း မရှိ) peek(): ElementType | undefined { // Type က pop လိုပဲ return this.items[this.items.length - 1]; }}
// အခု ဒီ Generic Stack Class ကို အသုံးပြုပြီး Stack Object တွေ ဖန်တီးကြည့်မယ်
// number တွေပဲ ထည့်လို့ရမယ့် Stack တစ်ခု ဖန်တီးမယ်let numberStack = new Stack<number>(); // Stack<number> လို့ သတ်မှတ်လိုက်တော့ ElementType ဟာ number လုံးလုံး ဖြစ်သွားပြီ
numberStack.push(10); // number ထည့်လို့ရတယ်numberStack.push(20); // number ထပ်ထည့်လို့ရတယ်
let lastNumber = numberStack.pop(); // pop လုပ်လိုက်ရင် number | undefined ပြန်ထွက်မယ်။ တကယ်တော့ 20 (number) ပြန်ထွက်လာတယ်console.log(lastNumber); // Output: 20
// string တွေပဲ ထည့်လို့ရမယ့် Stack တစ်ခု ဖန်တီးမယ်let stringStack = new Stack<string>(); // Stack<string> လို့ သတ်မှတ်လိုက်တော့ ElementType က string ဖြစ်သွားပြီ
stringStack.push("first"); // string ထည့်လို့ရတယ်stringStack.push("second"); // string ထပ်ထည့်လို့ရတယ်
let lastString = stringStack.pop(); // pop လုပ်လိုက်ရင် string | undefined ပြန်ထွက်မယ်။ တကယ်တော့ "second" (string) ပြန်ထွက်လာတယ်console.log(lastString); // Output: "second"
// stringStack.push(123); // ERROR: stringStack က string တွေအတွက် သီးသန့် Stack ဖြစ်လို့ number သွားထည့်လို့ မရတော့ဘူး။ TypeScript က ချက်ချင်း Error ပြပါမယ်!Class ထဲက Method တွေဖြစ်တဲ့
push,pop,peekတွေ အားလုံးကလည်း အဲ့ဒီ သတ်မှတ်လိုက်တဲ့ElementTypeType အတိုင်းပဲ အလုပ်လုပ်တော့ Type Safety ကို အပြည့်အဝ သေချာစေပါတယ်။numberStackထဲကိုstringသွားထည့်လို့ မရဘူး,stringStackထဲကိုnumberသွားထည့်လို့ မရဘူးပေါ့။ ဒါကြောင့် Code တွေ ပိုစိတ်ချရပြီး ပြန်သုံးရတာ ပိုအဆင်ပြေလာတာ ဖြစ်ပါတယ်။