Skip to content
GitHub

Generics တွေရဲ့ အသုံးဝင်ပုံ - Functions, Interfaces, Classes တွေမှာ သုံးခြင်း

Generics တွေက အရှေ့မှာ ပြောခဲ့တဲ့ identity Function လိုမျိုး ရိုးရှင်းတာတွေ တင်မကပါဘူး။ တခြား ရှုပ်ထွေးတဲ့ Function အမျိုးမျိုးမှာလည်း သုံးလို့ရပါတယ်။ ဥပမာအနေနဲ့ Data တစ်ခုခုကို လက်ခံပြီး အဲ့ဒီ Data တစ်ခုတည်း ပါဝင်တဲ့ Array အသစ် တစ်ခု ပြန်ထုတ်ပေးမယ့် Function လေးတစ်ခု ရေးကြည့်ရအောင်။

index.ts
// 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" }]

Interfaces တွေက Object တွေရဲ့ ပုံစံ (Shape) ကို သတ်မှတ်ပေးတယ်ဆိုတာ သိထားပြီးသားနော်။ ဒီ Interface တွေရဲ့ ပုံစံကိုလည်း Generics သုံးပြီး ပိုပြီး လိုက်လျောညီထွေ ဖြစ်အောင် ဖန်တီးလို့ရပါတယ်။ API ကနေ ပြန်လာတဲ့ Response တွေအတွက် Standard ပုံစံတစ်ခု သတ်မှတ်တာက Generic Interface ရဲ့ အကောင်းဆုံး လက်တွေ့ ဥပမာတစ်ခုပါပဲ။

service.ts
// 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 ကို သုံးလို့ရတယ်

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.ts
// 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 တွေ အားလုံးကလည်း အဲ့ဒီ သတ်မှတ်လိုက်တဲ့ ElementType Type အတိုင်းပဲ အလုပ်လုပ်တော့ Type Safety ကို အပြည့်အဝ သေချာစေပါတယ်။ numberStack ထဲကို string သွားထည့်လို့ မရဘူး, stringStack ထဲကို number သွားထည့်လို့ မရဘူးပေါ့။ ဒါကြောင့် Code တွေ ပိုစိတ်ချရပြီး ပြန်သုံးရတာ ပိုအဆင်ပြေလာတာ ဖြစ်ပါတယ်။